| 1 | /* Copyright (c) 2013-2016 the Civetweb developers |
| 2 | * Copyright (c) 2004-2013 Sergey Lyubka |
| 3 | * |
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 5 | * of this software and associated documentation files (the "Software"), to deal |
| 6 | * in the Software without restriction, including without limitation the rights |
| 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 8 | * copies of the Software, and to permit persons to whom the Software is |
| 9 | * furnished to do so, subject to the following conditions: |
| 10 | * |
| 11 | * The above copyright notice and this permission notice shall be included in |
| 12 | * all copies or substantial portions of the Software. |
| 13 | * |
| 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 20 | * THE SOFTWARE. |
| 21 | */ |
| 22 | |
| 23 | |
| 24 | #if defined(_WIN32) |
| 25 | #if !defined(_CRT_SECURE_NO_WARNINGS) |
| 26 | #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */ |
| 27 | #endif |
| 28 | #ifndef _WIN32_WINNT /* defined for tdm-gcc so we can use getnameinfo */ |
| 29 | #define _WIN32_WINNT 0x0501 |
| 30 | #endif |
| 31 | #else |
| 32 | #if defined(__GNUC__) && !defined(_GNU_SOURCE) |
| 33 | #define _GNU_SOURCE /* for setgroups() */ |
| 34 | #endif |
| 35 | #if defined(__linux__) && !defined(_XOPEN_SOURCE) |
| 36 | #define _XOPEN_SOURCE 600 /* For flockfile() on Linux */ |
| 37 | #endif |
| 38 | #ifndef _LARGEFILE_SOURCE |
| 39 | #define _LARGEFILE_SOURCE /* For fseeko(), ftello() */ |
| 40 | #endif |
| 41 | #ifndef _FILE_OFFSET_BITS |
| 42 | #define _FILE_OFFSET_BITS 64 /* Use 64-bit file offsets by default */ |
| 43 | #endif |
| 44 | #ifndef __STDC_FORMAT_MACROS |
| 45 | #define __STDC_FORMAT_MACROS /* <inttypes.h> wants this for C++ */ |
| 46 | #endif |
| 47 | #ifndef __STDC_LIMIT_MACROS |
| 48 | #define __STDC_LIMIT_MACROS /* C++ wants that for INT64_MAX */ |
| 49 | #endif |
| 50 | #ifdef __sun |
| 51 | #define __EXTENSIONS__ /* to expose flockfile and friends in stdio.h */ |
| 52 | #define __inline inline /* not recognized on older compiler versions */ |
| 53 | #endif |
| 54 | #endif |
| 55 | |
| 56 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 57 | #define USE_TIMERS |
| 58 | #endif |
| 59 | |
| 60 | #if defined(_MSC_VER) |
| 61 | /* 'type cast' : conversion from 'int' to 'HANDLE' of greater size */ |
| 62 | #pragma warning(disable : 4306) |
| 63 | /* conditional expression is constant: introduced by FD_SET(..) */ |
| 64 | #pragma warning(disable : 4127) |
| 65 | /* non-constant aggregate initializer: issued due to missing C99 support */ |
| 66 | #pragma warning(disable : 4204) |
| 67 | /* padding added after data member */ |
| 68 | #pragma warning(disable : 4820) |
| 69 | /* not defined as a preprocessor macro, replacing with '0' for '#if/#elif' */ |
| 70 | #pragma warning(disable : 4668) |
| 71 | /* no function prototype given: converting '()' to '(void)' */ |
| 72 | #pragma warning(disable : 4255) |
| 73 | /* function has been selected for automatic inline expansion */ |
| 74 | #pragma warning(disable : 4711) |
| 75 | #endif |
| 76 | |
| 77 | |
| 78 | /* This code uses static_assert to check some conditions. |
| 79 | * Unfortunately some compilers still do not support it, so we have a |
| 80 | * replacement function here. */ |
| 81 | #if defined(_MSC_VER) && (_MSC_VER >= 1600) |
| 82 | #define mg_static_assert static_assert |
| 83 | #elif defined(__cplusplus) && (__cplusplus >= 201103L) |
| 84 | #define mg_static_assert static_assert |
| 85 | #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) |
| 86 | #define mg_static_assert _Static_assert |
| 87 | #else |
| 88 | char static_assert_replacement[1]; |
| 89 | #define mg_static_assert(cond, txt) \ |
| 90 |  extern char static_assert_replacement[(cond) ? 1 : -1] |
| 91 | #endif |
| 92 | |
| 93 | mg_static_assert(sizeof(int) == 4 || sizeof(int) == 8, |
| 94 | "int data type size check"); |
| 95 | mg_static_assert(sizeof(void *) == 4 || sizeof(void *) == 8, |
| 96 | "pointer data type size check"); |
| 97 | mg_static_assert(sizeof(void *) >= sizeof(int), "data type size check"); |
| 98 | /* mg_static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8, "size_t data |
| 99 | * type size check"); */ |
| 100 | |
| 101 | /* DTL -- including winsock2.h works better if lean and mean */ |
| 102 | #ifndef WIN32_LEAN_AND_MEAN |
| 103 | #define WIN32_LEAN_AND_MEAN |
| 104 | #endif |
| 105 | |
| 106 | #if defined(__SYMBIAN32__) |
| 107 | #define NO_SSL /* SSL is not supported */ |
| 108 | #define NO_CGI /* CGI is not supported */ |
| 109 | #define PATH_MAX FILENAME_MAX |
| 110 | #endif /* __SYMBIAN32__ */ |
| 111 | |
| 112 | |
| 113 | /* Include the header file here, so the CivetWeb interface is defined for the |
| 114 | * entire implementation, including the following forward definitions. */ |
| 115 | #include "civetweb.h" |
| 116 | |
| 117 | |
| 118 | #ifndef IGNORE_UNUSED_RESULT |
| 119 | #define IGNORE_UNUSED_RESULT(a) ((void)((a) && 1)) |
| 120 | #endif |
| 121 | |
| 122 | #ifndef _WIN32_WCE /* Some ANSI #includes are not available on Windows CE */ |
| 123 | #include <sys/types.h> |
| 124 | #include <sys/stat.h> |
| 125 | #include <errno.h> |
| 126 | #include <signal.h> |
| 127 | #include <fcntl.h> |
| 128 | #endif /* !_WIN32_WCE */ |
| 129 | |
| 130 | #ifdef __MACH__ |
| 131 | |
| 132 | #define CLOCK_MONOTONIC (1) |
| 133 | #define CLOCK_REALTIME (2) |
| 134 | |
| 135 | #include <sys/time.h> |
| 136 | #include <mach/clock.h> |
| 137 | #include <mach/mach.h> |
| 138 | #include <mach/mach_time.h> |
| 139 | #include <assert.h> |
| 140 | |
| 141 | |
| 142 | /* clock_gettime is not implemented on OSX */ |
| 143 | int clock_gettime(int clk_id, struct timespec *t); |
| 144 | |
| 145 | int |
| 146 | clock_gettime(int clk_id, struct timespec *t) |
| 147 | { |
| 148 |  memset(t, 0, sizeof(*t)); |
| 149 |  if (clk_id == CLOCK_REALTIME) { |
| 150 |    struct timeval now; |
| 151 |    int rv = gettimeofday(&now, NULL); |
| 152 |    if (rv) { |
| 153 |      return rv; |
| 154 |    } |
| 155 |    t->tv_sec = now.tv_sec; |
| 156 |    t->tv_nsec = now.tv_usec * 1000; |
| 157 |    return 0; |
| 158 | |
| 159 |  } else if (clk_id == CLOCK_MONOTONIC) { |
| 160 |    static uint64_t clock_start_time = 0; |
| 161 |    static mach_timebase_info_data_t timebase_ifo = {0, 0}; |
| 162 | |
| 163 |    uint64_t now = mach_absolute_time(); |
| 164 | |
| 165 |    if (clock_start_time == 0) { |
| 166 |      kern_return_t mach_status = mach_timebase_info(&timebase_ifo); |
| 167 | #if defined(DEBUG) |
| 168 |      assert(mach_status == KERN_SUCCESS); |
| 169 | #else |
| 170 |      /* appease "unused variable" warning for release builds */ |
| 171 |      (void)mach_status; |
| 172 | #endif |
| 173 |      clock_start_time = now; |
| 174 |    } |
| 175 | |
| 176 |    now = (uint64_t)((double)(now - clock_start_time) |
| 177 |     * (double)timebase_ifo.numer |
| 178 |     / (double)timebase_ifo.denom); |
| 179 | |
| 180 |    t->tv_sec = now / 1000000000; |
| 181 |    t->tv_nsec = now % 1000000000; |
| 182 |    return 0; |
| 183 |  } |
| 184 |  return -1; /* EINVAL - Clock ID is unknown */ |
| 185 | } |
| 186 | #endif |
| 187 | |
| 188 | |
| 189 | #include <time.h> |
| 190 | #include <stdlib.h> |
| 191 | #include <stdarg.h> |
| 192 | #include <assert.h> |
| 193 | #include <string.h> |
| 194 | #include <ctype.h> |
| 195 | #include <limits.h> |
| 196 | #include <stddef.h> |
| 197 | #include <stdio.h> |
| 198 | |
| 199 | |
| 200 | #ifndef MAX_WORKER_THREADS |
| 201 | #define MAX_WORKER_THREADS (1024 * 64) |
| 202 | #endif |
| 203 | #ifndef SOCKET_TIMEOUT_QUANTUM |
| 204 | #define SOCKET_TIMEOUT_QUANTUM (10000) |
| 205 | #endif |
| 206 | |
| 207 | mg_static_assert(MAX_WORKER_THREADS >= 1, |
| 208 | "worker threads must be a positive number"); |
| 209 | |
| 210 | #if defined(_WIN32) \ |
| 211 | && !defined(__SYMBIAN32__) /* WINDOWS / UNIX include block */ |
| 212 | #include <windows.h> |
| 213 | #include <winsock2.h> /* DTL add for SO_EXCLUSIVE */ |
| 214 | #include <ws2tcpip.h> |
| 215 | |
| 216 | typedef const char *SOCK_OPT_TYPE; |
| 217 | |
| 218 | #if !defined(PATH_MAX) |
| 219 | #define PATH_MAX (MAX_PATH) |
| 220 | #endif |
| 221 | |
| 222 | #if !defined(PATH_MAX) |
| 223 | #define PATH_MAX (4096) |
| 224 | #endif |
| 225 | |
| 226 | mg_static_assert(PATH_MAX >= 1, "path length must be a positive number"); |
| 227 | |
| 228 | #ifndef _IN_PORT_T |
| 229 | #ifndef in_port_t |
| 230 | #define in_port_t u_short |
| 231 | #endif |
| 232 | #endif |
| 233 | |
| 234 | #ifndef _WIN32_WCE |
| 235 | #include <process.h> |
| 236 | #include <direct.h> |
| 237 | #include <io.h> |
| 238 | #else /* _WIN32_WCE */ |
| 239 | #define NO_CGI /* WinCE has no pipes */ |
| 240 | |
| 241 | typedef long off_t; |
| 242 | |
| 243 | #define errno ((int)(GetLastError())) |
| 244 | #define strerror(x) (_ultoa(x, (char *)_alloca(sizeof(x) * 3), 10)) |
| 245 | #endif /* _WIN32_WCE */ |
| 246 | |
| 247 | #define MAKEUQUAD(lo, hi) \ |
| 248 |  ((uint64_t)(((uint32_t)(lo)) | ((uint64_t)((uint32_t)(hi))) << 32)) |
| 249 | #define RATE_DIFF (10000000) /* 100 nsecs */ |
| 250 | #define EPOCH_DIFF (MAKEUQUAD(0xd53e8000, 0x019db1de)) |
| 251 | #define SYS2UNIX_TIME(lo, hi) \ |
| 252 |  ((time_t)((MAKEUQUAD((lo), (hi)) - EPOCH_DIFF) / RATE_DIFF)) |
| 253 | |
| 254 | /* Visual Studio 6 does not know __func__ or __FUNCTION__ |
| 255 | * The rest of MS compilers use __FUNCTION__, not C99 __func__ |
| 256 | * Also use _strtoui64 on modern M$ compilers */ |
| 257 | #if defined(_MSC_VER) |
| 258 | #if (_MSC_VER < 1300) |
| 259 | #define STRX(x) #x |
| 260 | #define STR(x) STRX(x) |
| 261 | #define __func__ __FILE__ ":" STR(__LINE__) |
| 262 | #define strtoull(x, y, z) ((unsigned __int64)_atoi64(x)) |
| 263 | #define strtoll(x, y, z) (_atoi64(x)) |
| 264 | #else |
| 265 | #define __func__ __FUNCTION__ |
| 266 | #define strtoull(x, y, z) (_strtoui64(x, y, z)) |
| 267 | #define strtoll(x, y, z) (_strtoi64(x, y, z)) |
| 268 | #endif |
| 269 | #endif /* _MSC_VER */ |
| 270 | |
| 271 | #define ERRNO ((int)(GetLastError())) |
| 272 | #define NO_SOCKLEN_T |
| 273 | |
| 274 | #if defined(_WIN64) || defined(__MINGW64__) |
| 275 | #define SSL_LIB "ssleay64.dll" |
| 276 | #define CRYPTO_LIB "libeay64.dll" |
| 277 | #else |
| 278 | #define SSL_LIB "ssleay32.dll" |
| 279 | #define CRYPTO_LIB "libeay32.dll" |
| 280 | #endif |
| 281 | |
| 282 | #define O_NONBLOCK (0) |
| 283 | #ifndef W_OK |
| 284 | #define W_OK (2) /* http://msdn.microsoft.com/en-us/library/1w06ktdy.aspx */ |
| 285 | #endif |
| 286 | #if !defined(EWOULDBLOCK) |
| 287 | #define EWOULDBLOCK WSAEWOULDBLOCK |
| 288 | #endif /* !EWOULDBLOCK */ |
| 289 | #define _POSIX_ |
| 290 | #define INT64_FMT "I64d" |
| 291 | #define UINT64_FMT "I64u" |
| 292 | |
| 293 | #define WINCDECL __cdecl |
| 294 | #define SHUT_RD (0) |
| 295 | #define SHUT_WR (1) |
| 296 | #define SHUT_BOTH (2) |
| 297 | #define vsnprintf_impl _vsnprintf |
| 298 | #define access _access |
| 299 | #define mg_sleep(x) (Sleep(x)) |
| 300 | |
| 301 | #define pipe(x) _pipe(x, MG_BUF_LEN, _O_BINARY) |
| 302 | #ifndef popen |
| 303 | #define popen(x, y) (_popen(x, y)) |
| 304 | #endif |
| 305 | #ifndef pclose |
| 306 | #define pclose(x) (_pclose(x)) |
| 307 | #endif |
| 308 | #define close(x) (_close(x)) |
| 309 | #define dlsym(x, y) (GetProcAddress((HINSTANCE)(x), (y))) |
| 310 | #define RTLD_LAZY (0) |
| 311 | #define fseeko(x, y, z) (_lseeki64(_fileno(x), (y), (z)) == -1 ? -1 : 0) |
| 312 | #define fdopen(x, y) (_fdopen((x), (y))) |
| 313 | #define write(x, y, z) (_write((x), (y), (unsigned)z)) |
| 314 | #define read(x, y, z) (_read((x), (y), (unsigned)z)) |
| 315 | #define flockfile(x) (EnterCriticalSection(&global_log_file_lock)) |
| 316 | #define funlockfile(x) (LeaveCriticalSection(&global_log_file_lock)) |
| 317 | #define sleep(x) (Sleep((x)*1000)) |
| 318 | #define rmdir(x) (_rmdir(x)) |
| 319 | #define timegm(x) (_mkgmtime(x)) |
| 320 | |
| 321 | #if !defined(fileno) |
| 322 | #define fileno(x) (_fileno(x)) |
| 323 | #endif /* !fileno MINGW #defines fileno */ |
| 324 | |
| 325 | typedef HANDLE pthread_mutex_t; |
| 326 | typedef DWORD pthread_key_t; |
| 327 | typedef HANDLE pthread_t; |
| 328 | typedef struct { |
| 329 |  CRITICAL_SECTION threadIdSec; |
| 330 |  int waitingthreadcount; /* The number of threads queued. */ |
| 331 |  pthread_t *waitingthreadhdls; /* The thread handles. */ |
| 332 | } pthread_cond_t; |
| 333 | |
| 334 | #ifndef __clockid_t_defined |
| 335 | typedef DWORD clockid_t; |
| 336 | #endif |
| 337 | #ifndef CLOCK_MONOTONIC |
| 338 | #define CLOCK_MONOTONIC (1) |
| 339 | #endif |
| 340 | #ifndef CLOCK_REALTIME |
| 341 | #define CLOCK_REALTIME (2) |
| 342 | #endif |
| 343 | |
| 344 | #if defined(_MSC_VER) && (_MSC_VER >= 1900) |
| 345 | #define _TIMESPEC_DEFINED |
| 346 | #endif |
| 347 | #ifndef _TIMESPEC_DEFINED |
| 348 | struct timespec { |
| 349 |  time_t tv_sec; /* seconds */ |
| 350 |  long tv_nsec; /* nanoseconds */ |
| 351 | }; |
| 352 | #endif |
| 353 | |
| 354 | #define pid_t HANDLE /* MINGW typedefs pid_t to int. Using #define here. */ |
| 355 | |
| 356 | static int pthread_mutex_lock(pthread_mutex_t *); |
| 357 | static int pthread_mutex_unlock(pthread_mutex_t *); |
| 358 | static void path_to_unicode(const struct mg_connection *conn, |
| 359 | const char *path, |
| 360 | wchar_t *wbuf, |
| 361 | size_t wbuf_len); |
| 362 | struct file; |
| 363 | static const char * |
| 364 | mg_fgets(char *buf, size_t size, struct file *filep, char **p); |
| 365 | |
| 366 | |
| 367 | #if defined(HAVE_STDINT) |
| 368 | #include <stdint.h> |
| 369 | #else |
| 370 | typedef unsigned char uint8_t; |
| 371 | typedef unsigned short uint16_t; |
| 372 | typedef unsigned int uint32_t; |
| 373 | typedef unsigned __int64 uint64_t; |
| 374 | typedef __int64 int64_t; |
| 375 | #define INT64_MAX (9223372036854775807) |
| 376 | #endif /* HAVE_STDINT */ |
| 377 | |
| 378 | /* POSIX dirent interface */ |
| 379 | struct dirent { |
| 380 |  char d_name[PATH_MAX]; |
| 381 | }; |
| 382 | |
| 383 | typedef struct DIR { |
| 384 |  HANDLE handle; |
| 385 |  WIN32_FIND_DATAW info; |
| 386 |  struct dirent result; |
| 387 | } DIR; |
| 388 | |
| 389 | #if defined(_WIN32) && !defined(POLLIN) |
| 390 | #ifndef HAVE_POLL |
| 391 | struct pollfd { |
| 392 |  SOCKET fd; |
| 393 |  short events; |
| 394 |  short revents; |
| 395 | }; |
| 396 | #define POLLIN (0x0300) |
| 397 | #endif |
| 398 | #endif |
| 399 | |
| 400 | /* Mark required libraries */ |
| 401 | #if defined(_MSC_VER) |
| 402 | #pragma comment(lib, "Ws2_32.lib") |
| 403 | #endif |
| 404 | |
| 405 | #else /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \ |
| 406 | block */ |
| 407 | |
| 408 | #include <sys/wait.h> |
| 409 | #include <sys/socket.h> |
| 410 | #include <sys/poll.h> |
| 411 | #include <netinet/in.h> |
| 412 | #include <arpa/inet.h> |
| 413 | #include <sys/time.h> |
| 414 | #include <sys/utsname.h> |
| 415 | #include <stdint.h> |
| 416 | #include <inttypes.h> |
| 417 | #include <netdb.h> |
| 418 | #include <netinet/tcp.h> |
| 419 | typedef const void *SOCK_OPT_TYPE; |
| 420 | |
| 421 | #if defined(ANDROID) |
| 422 | typedef unsigned short int in_port_t; |
| 423 | #endif |
| 424 | |
| 425 | #include <pwd.h> |
| 426 | #include <unistd.h> |
| 427 | #include <grp.h> |
| 428 | #include <dirent.h> |
| 429 | #define vsnprintf_impl vsnprintf |
| 430 | |
| 431 | #if !defined(NO_SSL_DL) && !defined(NO_SSL) |
| 432 | #include <dlfcn.h> |
| 433 | #endif |
| 434 | #include <pthread.h> |
| 435 | #if defined(__MACH__) |
| 436 | #define SSL_LIB "libssl.dylib" |
| 437 | #define CRYPTO_LIB "libcrypto.dylib" |
| 438 | #else |
| 439 | #if !defined(SSL_LIB) |
| 440 | #define SSL_LIB "libssl.so" |
| 441 | #endif |
| 442 | #if !defined(CRYPTO_LIB) |
| 443 | #define CRYPTO_LIB "libcrypto.so" |
| 444 | #endif |
| 445 | #endif |
| 446 | #ifndef O_BINARY |
| 447 | #define O_BINARY (0) |
| 448 | #endif /* O_BINARY */ |
| 449 | #define closesocket(a) (close(a)) |
| 450 | #define mg_mkdir(conn, path, mode) (mkdir(path, mode)) |
| 451 | #define mg_remove(conn, x) (remove(x)) |
| 452 | #define mg_sleep(x) (usleep((x)*1000)) |
| 453 | #define mg_opendir(conn, x) (opendir(x)) |
| 454 | #define mg_closedir(x) (closedir(x)) |
| 455 | #define mg_readdir(x) (readdir(x)) |
| 456 | #define ERRNO (errno) |
| 457 | #define INVALID_SOCKET (-1) |
| 458 | #define INT64_FMT PRId64 |
| 459 | #define UINT64_FMT PRIu64 |
| 460 | typedef int SOCKET; |
| 461 | #define WINCDECL |
| 462 | |
| 463 | #if defined(__hpux) |
| 464 | /* HPUX 11 does not have monotonic, fall back to realtime */ |
| 465 | #ifndef CLOCK_MONOTONIC |
| 466 | #define CLOCK_MONOTONIC CLOCK_REALTIME |
| 467 | #endif |
| 468 | |
| 469 | /* HPUX defines socklen_t incorrectly as size_t which is 64bit on |
| 470 | * Itanium. Without defining _XOPEN_SOURCE or _XOPEN_SOURCE_EXTENDED |
| 471 | * the prototypes use int* rather than socklen_t* which matches the |
| 472 | * actual library expectation. When called with the wrong size arg |
| 473 | * accept() returns a zero client inet addr and check_acl() always |
| 474 | * fails. Since socklen_t is widely used below, just force replace |
| 475 | * their typedef with int. - DTL |
| 476 | */ |
| 477 | #define socklen_t int |
| 478 | #endif /* hpux */ |
| 479 | |
| 480 | #endif /* defined(_WIN32) && !defined(__SYMBIAN32__) - WINDOWS / UNIX include \ |
| 481 | block */ |
| 482 | |
| 483 | /* va_copy should always be a macro, C99 and C++11 - DTL */ |
| 484 | #ifndef va_copy |
| 485 | #define va_copy(x, y) ((x) = (y)) |
| 486 | #endif |
| 487 | |
| 488 | #ifdef _WIN32 |
| 489 | /* Create substitutes for POSIX functions in Win32. */ |
| 490 | |
| 491 | #if defined(__MINGW32__) |
| 492 | /* Show no warning in case system functions are not used. */ |
| 493 | #pragma GCC diagnostic push |
| 494 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 495 | #endif |
| 496 | |
| 497 | |
| 498 | static CRITICAL_SECTION global_log_file_lock; |
| 499 | static DWORD |
| 500 | pthread_self(void) |
| 501 | { |
| 502 |  return GetCurrentThreadId(); |
| 503 | } |
| 504 | |
| 505 | |
| 506 | static int |
| 507 | pthread_key_create( |
| 508 | pthread_key_t *key, |
| 509 | void (*_ignored)(void *) /* destructor not supported for Windows */ |
| 510 | ) |
| 511 | { |
| 512 |  (void)_ignored; |
| 513 | |
| 514 |  if ((key != 0)) { |
| 515 |    *key = TlsAlloc(); |
| 516 |    return (*key != TLS_OUT_OF_INDEXES) ? 0 : -1; |
| 517 |  } |
| 518 |  return -2; |
| 519 | } |
| 520 | |
| 521 | |
| 522 | static int |
| 523 | pthread_key_delete(pthread_key_t key) |
| 524 | { |
| 525 |  return TlsFree(key) ? 0 : 1; |
| 526 | } |
| 527 | |
| 528 | |
| 529 | static int |
| 530 | pthread_setspecific(pthread_key_t key, void *value) |
| 531 | { |
| 532 |  return TlsSetValue(key, value) ? 0 : 1; |
| 533 | } |
| 534 | |
| 535 | |
| 536 | static void * |
| 537 | pthread_getspecific(pthread_key_t key) |
| 538 | { |
| 539 |  return TlsGetValue(key); |
| 540 | } |
| 541 | |
| 542 | #if defined(__MINGW32__) |
| 543 | /* Enable unused function warning again */ |
| 544 | #pragma GCC diagnostic pop |
| 545 | #endif |
| 546 | |
| 547 | static struct pthread_mutex_undefined_struct *pthread_mutex_attr = NULL; |
| 548 | #else |
| 549 | static pthread_mutexattr_t pthread_mutex_attr; |
| 550 | #endif /* _WIN32 */ |
| 551 | |
| 552 | |
| 553 | #define PASSWORDS_FILE_NAME ".htpasswd" |
| 554 | #define CGI_ENVIRONMENT_SIZE (4096) |
| 555 | #define MAX_CGI_ENVIR_VARS (256) |
| 556 | #define MG_BUF_LEN (8192) |
| 557 | |
| 558 | #ifndef MAX_REQUEST_SIZE |
| 559 | #define MAX_REQUEST_SIZE (16384) |
| 560 | #endif |
| 561 | |
| 562 | mg_static_assert(MAX_REQUEST_SIZE >= 256, |
| 563 | "request size length must be a positive number"); |
| 564 | |
| 565 | #define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) |
| 566 | |
| 567 | #if !defined(DEBUG_TRACE) |
| 568 | #if defined(DEBUG) |
| 569 | |
| 570 | |
| 571 | static void DEBUG_TRACE_FUNC(const char *func, |
| 572 | unsigned line, |
| 573 | PRINTF_FORMAT_STRING(const char *fmt), |
| 574 | ...) PRINTF_ARGS(3, 4); |
| 575 | |
| 576 | static void |
| 577 | DEBUG_TRACE_FUNC(const char *func, unsigned line, const char *fmt, ...) |
| 578 | { |
| 579 |  va_list args; |
| 580 |  flockfile(stdout); |
| 581 |  printf("*** %lu.%p.%s.%u: ", |
| 582 |   (unsigned long)time(NULL), |
| 583 |   (void *)pthread_self(), |
| 584 |   func, |
| 585 |   line); |
| 586 |  va_start(args, fmt); |
| 587 |  vprintf(fmt, args); |
| 588 |  va_end(args); |
| 589 |  putchar('\n'); |
| 590 |  fflush(stdout); |
| 591 |  funlockfile(stdout); |
| 592 | } |
| 593 | |
| 594 | #define DEBUG_TRACE(fmt, ...) \ |
| 595 |  DEBUG_TRACE_FUNC(__func__, __LINE__, fmt, __VA_ARGS__) |
| 596 | |
| 597 | #else |
| 598 | #define DEBUG_TRACE(fmt, ...) \ |
| 599 |  do { \ |
| 600 |  } while (0) |
| 601 | #endif /* DEBUG */ |
| 602 | #endif /* DEBUG_TRACE */ |
| 603 | |
| 604 | #if defined(MEMORY_DEBUGGING) |
| 605 | unsigned long mg_memory_debug_blockCount = 0; |
| 606 | unsigned long mg_memory_debug_totalMemUsed = 0; |
| 607 | |
| 608 | |
| 609 | static void * |
| 610 | mg_malloc_ex(size_t size, const char *file, unsigned line) |
| 611 | { |
| 612 |  void *data = malloc(size + sizeof(size_t)); |
| 613 |  void *memory = 0; |
| 614 |  char mallocStr[256]; |
| 615 | |
| 616 |  if (data) { |
| 617 |    *(size_t *)data = size; |
| 618 |    mg_memory_debug_totalMemUsed += size; |
| 619 |    mg_memory_debug_blockCount++; |
| 620 |    memory = (void *)(((char *)data) + sizeof(size_t)); |
| 621 |  } |
| 622 | |
| 623 |  sprintf(mallocStr, |
| 624 |   "MEM: %p %5lu alloc %7lu %4lu --- %s:%u\n", |
| 625 |   memory, |
| 626 |   (unsigned long)size, |
| 627 |   mg_memory_debug_totalMemUsed, |
| 628 |   mg_memory_debug_blockCount, |
| 629 |   file, |
| 630 |   line); |
| 631 | #if defined(_WIN32) |
| 632 |  OutputDebugStringA(mallocStr); |
| 633 | #else |
| 634 |  DEBUG_TRACE("%s", mallocStr); |
| 635 | #endif |
| 636 | |
| 637 |  return memory; |
| 638 | } |
| 639 | |
| 640 | |
| 641 | static void * |
| 642 | mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line) |
| 643 | { |
| 644 |  void *data = mg_malloc_ex(size * count, file, line); |
| 645 |  if (data) { |
| 646 |    memset(data, 0, size); |
| 647 |  } |
| 648 |  return data; |
| 649 | } |
| 650 | |
| 651 | |
| 652 | static void |
| 653 | mg_free_ex(void *memory, const char *file, unsigned line) |
| 654 | { |
| 655 |  char mallocStr[256]; |
| 656 |  void *data = (void *)(((char *)memory) - sizeof(size_t)); |
| 657 |  size_t size; |
| 658 | |
| 659 |  if (memory) { |
| 660 |    size = *(size_t *)data; |
| 661 |    mg_memory_debug_totalMemUsed -= size; |
| 662 |    mg_memory_debug_blockCount--; |
| 663 |    sprintf(mallocStr, |
| 664 |     "MEM: %p %5lu free %7lu %4lu --- %s:%u\n", |
| 665 |     memory, |
| 666 |     (unsigned long)size, |
| 667 |     mg_memory_debug_totalMemUsed, |
| 668 |     mg_memory_debug_blockCount, |
| 669 |     file, |
| 670 |     line); |
| 671 | #if defined(_WIN32) |
| 672 |    OutputDebugStringA(mallocStr); |
| 673 | #else |
| 674 |    DEBUG_TRACE("%s", mallocStr); |
| 675 | #endif |
| 676 | |
| 677 |    free(data); |
| 678 |  } |
| 679 | } |
| 680 | |
| 681 | |
| 682 | static void * |
| 683 | mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line) |
| 684 | { |
| 685 |  char mallocStr[256]; |
| 686 |  void *data; |
| 687 |  void *_realloc; |
| 688 |  size_t oldsize; |
| 689 | |
| 690 |  if (newsize) { |
| 691 |    if (memory) { |
| 692 |      data = (void *)(((char *)memory) - sizeof(size_t)); |
| 693 |      oldsize = *(size_t *)data; |
| 694 |      _realloc = realloc(data, newsize + sizeof(size_t)); |
| 695 |      if (_realloc) { |
| 696 |        data = _realloc; |
| 697 |        mg_memory_debug_totalMemUsed -= oldsize; |
| 698 |        sprintf(mallocStr, |
| 699 |         "MEM: %p %5lu r-free %7lu %4lu --- %s:%u\n", |
| 700 |         memory, |
| 701 |         (unsigned long)oldsize, |
| 702 |         mg_memory_debug_totalMemUsed, |
| 703 |         mg_memory_debug_blockCount, |
| 704 |         file, |
| 705 |         line); |
| 706 | #if defined(_WIN32) |
| 707 |        OutputDebugStringA(mallocStr); |
| 708 | #else |
| 709 |        DEBUG_TRACE("%s", mallocStr); |
| 710 | #endif |
| 711 |        mg_memory_debug_totalMemUsed += newsize; |
| 712 |        sprintf(mallocStr, |
| 713 |         "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n", |
| 714 |         memory, |
| 715 |         (unsigned long)newsize, |
| 716 |         mg_memory_debug_totalMemUsed, |
| 717 |         mg_memory_debug_blockCount, |
| 718 |         file, |
| 719 |         line); |
| 720 | #if defined(_WIN32) |
| 721 |        OutputDebugStringA(mallocStr); |
| 722 | #else |
| 723 |        DEBUG_TRACE("%s", mallocStr); |
| 724 | #endif |
| 725 |        *(size_t *)data = newsize; |
| 726 |        data = (void *)(((char *)data) + sizeof(size_t)); |
| 727 |      } else { |
| 728 | #if defined(_WIN32) |
| 729 |        OutputDebugStringA("MEM: realloc failed\n"); |
| 730 | #else |
| 731 |        DEBUG_TRACE("%s", "MEM: realloc failed\n"); |
| 732 | #endif |
| 733 |        return _realloc; |
| 734 |      } |
| 735 |    } else { |
| 736 |      data = mg_malloc_ex(newsize, file, line); |
| 737 |    } |
| 738 |  } else { |
| 739 |    data = 0; |
| 740 |    mg_free_ex(memory, file, line); |
| 741 |  } |
| 742 | |
| 743 |  return data; |
| 744 | } |
| 745 | |
| 746 | #define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__) |
| 747 | #define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__) |
| 748 | #define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__) |
| 749 | #define mg_free(a) mg_free_ex(a, __FILE__, __LINE__) |
| 750 | |
| 751 | #else |
| 752 | |
| 753 | static __inline void * |
| 754 | mg_malloc(size_t a) |
| 755 | { |
| 756 |  return malloc(a); |
| 757 | } |
| 758 | |
| 759 | static __inline void * |
| 760 | mg_calloc(size_t a, size_t b) |
| 761 | { |
| 762 |  return calloc(a, b); |
| 763 | } |
| 764 | |
| 765 | static __inline void * |
| 766 | mg_realloc(void *a, size_t b) |
| 767 | { |
| 768 |  return realloc(a, b); |
| 769 | } |
| 770 | |
| 771 | static __inline void |
| 772 | mg_free(void *a) |
| 773 | { |
| 774 |  free(a); |
| 775 | } |
| 776 | |
| 777 | #endif |
| 778 | |
| 779 | |
| 780 | static void mg_vsnprintf(const struct mg_connection *conn, |
| 781 | int *truncated, |
| 782 | char *buf, |
| 783 | size_t buflen, |
| 784 | const char *fmt, |
| 785 | va_list ap); |
| 786 | |
| 787 | static void mg_snprintf(const struct mg_connection *conn, |
| 788 | int *truncated, |
| 789 | char *buf, |
| 790 | size_t buflen, |
| 791 | PRINTF_FORMAT_STRING(const char *fmt), |
| 792 | ...) PRINTF_ARGS(5, 6); |
| 793 | |
| 794 | /* This following lines are just meant as a reminder to use the mg-functions |
| 795 | * for memory management */ |
| 796 | #ifdef malloc |
| 797 | #undef malloc |
| 798 | #endif |
| 799 | #ifdef calloc |
| 800 | #undef calloc |
| 801 | #endif |
| 802 | #ifdef realloc |
| 803 | #undef realloc |
| 804 | #endif |
| 805 | #ifdef free |
| 806 | #undef free |
| 807 | #endif |
| 808 | #ifdef snprintf |
| 809 | #undef snprintf |
| 810 | #endif |
| 811 | #ifdef vsnprintf |
| 812 | #undef vsnprintf |
| 813 | #endif |
| 814 | #define malloc DO_NOT_USE_THIS_FUNCTION__USE_mg_malloc |
| 815 | #define calloc DO_NOT_USE_THIS_FUNCTION__USE_mg_calloc |
| 816 | #define realloc DO_NOT_USE_THIS_FUNCTION__USE_mg_realloc |
| 817 | #define free DO_NOT_USE_THIS_FUNCTION__USE_mg_free |
| 818 | #define snprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_snprintf |
| 819 | #ifdef _WIN32 /* vsnprintf must not be used in any system, * \ |
| 820 | * but this define only works well for Windows. */ |
| 821 | #define vsnprintf DO_NOT_USE_THIS_FUNCTION__USE_mg_vsnprintf |
| 822 | #endif |
| 823 | |
| 824 | #define MD5_STATIC static |
| 825 | #include "md5.inl" |
| 826 | |
| 827 | /* Darwin prior to 7.0 and Win32 do not have socklen_t */ |
| 828 | #ifdef NO_SOCKLEN_T |
| 829 | typedef int socklen_t; |
| 830 | #endif /* NO_SOCKLEN_T */ |
| 831 | #define _DARWIN_UNLIMITED_SELECT |
| 832 | |
| 833 | #define IP_ADDR_STR_LEN (50) /* IPv6 hex string is 46 chars */ |
| 834 | |
| 835 | #if !defined(MSG_NOSIGNAL) |
| 836 | #define MSG_NOSIGNAL (0) |
| 837 | #endif |
| 838 | |
| 839 | #if !defined(SOMAXCONN) |
| 840 | #define SOMAXCONN (100) |
| 841 | #endif |
| 842 | |
| 843 | /* Size of the accepted socket queue */ |
| 844 | #if !defined(MGSQLEN) |
| 845 | #define MGSQLEN (20) |
| 846 | #endif |
| 847 | |
| 848 | #if defined(NO_SSL_DL) |
| 849 | #include <openssl/ssl.h> |
| 850 | #include <openssl/err.h> |
| 851 | #include <openssl/crypto.h> |
| 852 | #include <openssl/x509.h> |
| 853 | #include <openssl/pem.h> |
| 854 | #else |
| 855 | /* SSL loaded dynamically from DLL. |
| 856 | * I put the prototypes here to be independent from OpenSSL source |
| 857 | * installation. */ |
| 858 | |
| 859 | typedef struct ssl_st SSL; |
| 860 | typedef struct ssl_method_st SSL_METHOD; |
| 861 | typedef struct ssl_ctx_st SSL_CTX; |
| 862 | typedef struct x509_store_ctx_st X509_STORE_CTX; |
| 863 | |
| 864 | #define SSL_CTRL_OPTIONS (32) |
| 865 | #define SSL_CTRL_CLEAR_OPTIONS (77) |
| 866 | #define SSL_CTRL_SET_ECDH_AUTO (94) |
| 867 | |
| 868 | #define SSL_VERIFY_NONE (0) |
| 869 | #define SSL_VERIFY_PEER (1) |
| 870 | #define SSL_VERIFY_FAIL_IF_NO_PEER_CERT (2) |
| 871 | #define SSL_VERIFY_CLIENT_ONCE (4) |
| 872 | #define SSL_OP_ALL ((long)(0x80000BFFUL)) |
| 873 | #define SSL_OP_NO_SSLv2 (0x01000000L) |
| 874 | #define SSL_OP_NO_SSLv3 (0x02000000L) |
| 875 | #define SSL_OP_NO_TLSv1 (0x04000000L) |
| 876 | #define SSL_OP_NO_TLSv1_2 (0x08000000L) |
| 877 | #define SSL_OP_NO_TLSv1_1 (0x10000000L) |
| 878 | #define SSL_OP_SINGLE_DH_USE (0x00100000L) |
| 879 | |
| 880 | struct ssl_func { |
| 881 |  const char *name; /* SSL function name */ |
| 882 |  void (*ptr)(void); /* Function pointer */ |
| 883 | }; |
| 884 | |
| 885 | #define SSL_free (*(void (*)(SSL *))ssl_sw[0].ptr) |
| 886 | #define SSL_accept (*(int (*)(SSL *))ssl_sw[1].ptr) |
| 887 | #define SSL_connect (*(int (*)(SSL *))ssl_sw[2].ptr) |
| 888 | #define SSL_read (*(int (*)(SSL *, void *, int))ssl_sw[3].ptr) |
| 889 | #define SSL_write (*(int (*)(SSL *, const void *, int))ssl_sw[4].ptr) |
| 890 | #define SSL_get_error (*(int (*)(SSL *, int))ssl_sw[5].ptr) |
| 891 | #define SSL_set_fd (*(int (*)(SSL *, SOCKET))ssl_sw[6].ptr) |
| 892 | #define SSL_new (*(SSL * (*)(SSL_CTX *))ssl_sw[7].ptr) |
| 893 | #define SSL_CTX_new (*(SSL_CTX * (*)(SSL_METHOD *))ssl_sw[8].ptr) |
| 894 | #define SSLv23_server_method (*(SSL_METHOD * (*)(void))ssl_sw[9].ptr) |
| 895 | #define SSL_library_init (*(int (*)(void))ssl_sw[10].ptr) |
| 896 | #define SSL_CTX_use_PrivateKey_file \ |
| 897 |  (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[11].ptr) |
| 898 | #define SSL_CTX_use_certificate_file \ |
| 899 |  (*(int (*)(SSL_CTX *, const char *, int))ssl_sw[12].ptr) |
| 900 | #define SSL_CTX_set_default_passwd_cb \ |
| 901 |  (*(void (*)(SSL_CTX *, mg_callback_t))ssl_sw[13].ptr) |
| 902 | #define SSL_CTX_free (*(void (*)(SSL_CTX *))ssl_sw[14].ptr) |
| 903 | #define SSL_load_error_strings (*(void (*)(void))ssl_sw[15].ptr) |
| 904 | #define SSL_CTX_use_certificate_chain_file \ |
| 905 |  (*(int (*)(SSL_CTX *, const char *))ssl_sw[16].ptr) |
| 906 | #define SSLv23_client_method (*(SSL_METHOD * (*)(void))ssl_sw[17].ptr) |
| 907 | #define SSL_pending (*(int (*)(SSL *))ssl_sw[18].ptr) |
| 908 | #define SSL_CTX_set_verify \ |
| 909 |  (*(void (*)(SSL_CTX *, \ |
| 910 |   int, \ |
| 911 |   int (*verify_callback)(int, X509_STORE_CTX *)))ssl_sw[19].ptr) |
| 912 | #define SSL_shutdown (*(int (*)(SSL *))ssl_sw[20].ptr) |
| 913 | #define SSL_CTX_load_verify_locations \ |
| 914 |  (*(int (*)(SSL_CTX *, const char *, const char *))ssl_sw[21].ptr) |
| 915 | #define SSL_CTX_set_default_verify_paths (*(int (*)(SSL_CTX *))ssl_sw[22].ptr) |
| 916 | #define SSL_CTX_set_verify_depth (*(void (*)(SSL_CTX *, int))ssl_sw[23].ptr) |
| 917 | #define SSL_get_peer_certificate (*(X509 * (*)(SSL *))ssl_sw[24].ptr) |
| 918 | #define SSL_get_version (*(const char *(*)(SSL *))ssl_sw[25].ptr) |
| 919 | #define SSL_get_current_cipher (*(SSL_CIPHER * (*)(SSL *))ssl_sw[26].ptr) |
| 920 | #define SSL_CIPHER_get_name \ |
| 921 |  (*(const char *(*)(const SSL_CIPHER *))ssl_sw[27].ptr) |
| 922 | #define SSL_CTX_check_private_key (*(int (*)(SSL_CTX *))ssl_sw[28].ptr) |
| 923 | #define SSL_CTX_set_session_id_context \ |
| 924 |  (*(int (*)(SSL_CTX *, const unsigned char *, unsigned int))ssl_sw[29].ptr) |
| 925 | #define SSL_CTX_ctrl (*(long (*)(SSL_CTX *, int, long, void *))ssl_sw[30].ptr) |
| 926 | #define SSL_CTX_set_cipher_list \ |
| 927 |  (*(int (*)(SSL_CTX *, const char *))ssl_sw[31].ptr) |
| 928 | #define SSL_CTX_set_options(ctx, op) \ |
| 929 |  SSL_CTX_ctrl((ctx), SSL_CTRL_OPTIONS, (op), NULL) |
| 930 | #define SSL_CTX_clear_options(ctx, op) \ |
| 931 |  SSL_CTX_ctrl((ctx), SSL_CTRL_CLEAR_OPTIONS, (op), NULL) |
| 932 | #define SSL_CTX_set_ecdh_auto(ctx, onoff) \ |
| 933 |  SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, NULL) |
| 934 | |
| 935 | #define CRYPTO_num_locks (*(int (*)(void))crypto_sw[0].ptr) |
| 936 | #define CRYPTO_set_locking_callback \ |
| 937 |  (*(void (*)(void (*)(int, int, const char *, int)))crypto_sw[1].ptr) |
| 938 | #define CRYPTO_set_id_callback \ |
| 939 |  (*(void (*)(unsigned long (*)(void)))crypto_sw[2].ptr) |
| 940 | #define ERR_get_error (*(unsigned long (*)(void))crypto_sw[3].ptr) |
| 941 | #define ERR_error_string (*(char *(*)(unsigned long, char *))crypto_sw[4].ptr) |
| 942 | #define ERR_remove_state (*(void (*)(unsigned long))crypto_sw[5].ptr) |
| 943 | #define ERR_free_strings (*(void (*)(void))crypto_sw[6].ptr) |
| 944 | #define ENGINE_cleanup (*(void (*)(void))crypto_sw[7].ptr) |
| 945 | #define CONF_modules_unload (*(void (*)(int))crypto_sw[8].ptr) |
| 946 | #define CRYPTO_cleanup_all_ex_data (*(void (*)(void))crypto_sw[9].ptr) |
| 947 | #define EVP_cleanup (*(void (*)(void))crypto_sw[10].ptr) |
| 948 | |
| 949 | |
| 950 | /* set_ssl_option() function updates this array. |
| 951 | * It loads SSL library dynamically and changes NULLs to the actual addresses |
| 952 | * of respective functions. The macros above (like SSL_connect()) are really |
| 953 | * just calling these functions indirectly via the pointer. */ |
| 954 | static struct ssl_func ssl_sw[] = {{"SSL_free", NULL}, |
| 955 | {"SSL_accept", NULL}, |
| 956 | {"SSL_connect", NULL}, |
| 957 | {"SSL_read", NULL}, |
| 958 | {"SSL_write", NULL}, |
| 959 | {"SSL_get_error", NULL}, |
| 960 | {"SSL_set_fd", NULL}, |
| 961 | {"SSL_new", NULL}, |
| 962 | {"SSL_CTX_new", NULL}, |
| 963 | {"SSLv23_server_method", NULL}, |
| 964 | {"SSL_library_init", NULL}, |
| 965 | {"SSL_CTX_use_PrivateKey_file", NULL}, |
| 966 | {"SSL_CTX_use_certificate_file", NULL}, |
| 967 | {"SSL_CTX_set_default_passwd_cb", NULL}, |
| 968 | {"SSL_CTX_free", NULL}, |
| 969 | {"SSL_load_error_strings", NULL}, |
| 970 | {"SSL_CTX_use_certificate_chain_file", NULL}, |
| 971 | {"SSLv23_client_method", NULL}, |
| 972 | {"SSL_pending", NULL}, |
| 973 | {"SSL_CTX_set_verify", NULL}, |
| 974 | {"SSL_shutdown", NULL}, |
| 975 | {"SSL_CTX_load_verify_locations", NULL}, |
| 976 | {"SSL_CTX_set_default_verify_paths", NULL}, |
| 977 | {"SSL_CTX_set_verify_depth", NULL}, |
| 978 | {"SSL_get_peer_certificate", NULL}, |
| 979 | {"SSL_get_version", NULL}, |
| 980 | {"SSL_get_current_cipher", NULL}, |
| 981 | {"SSL_CIPHER_get_name", NULL}, |
| 982 | {"SSL_CTX_check_private_key", NULL}, |
| 983 | {"SSL_CTX_set_session_id_context", NULL}, |
| 984 | {"SSL_CTX_ctrl", NULL}, |
| 985 | {"SSL_CTX_set_cipher_list", NULL}, |
| 986 | {NULL, NULL}}; |
| 987 | |
| 988 | |
| 989 | /* Similar array as ssl_sw. These functions could be located in different |
| 990 | * lib. */ |
| 991 | #if !defined(NO_SSL) |
| 992 | static struct ssl_func crypto_sw[] = {{"CRYPTO_num_locks", NULL}, |
| 993 | {"CRYPTO_set_locking_callback", NULL}, |
| 994 | {"CRYPTO_set_id_callback", NULL}, |
| 995 | {"ERR_get_error", NULL}, |
| 996 | {"ERR_error_string", NULL}, |
| 997 | {"ERR_remove_state", NULL}, |
| 998 | {"ERR_free_strings", NULL}, |
| 999 | {"ENGINE_cleanup", NULL}, |
| 1000 | {"CONF_modules_unload", NULL}, |
| 1001 | {"CRYPTO_cleanup_all_ex_data", NULL}, |
| 1002 | {"EVP_cleanup", NULL}, |
| 1003 | {NULL, NULL}}; |
| 1004 | #endif /* NO_SSL */ |
| 1005 | #endif /* NO_SSL_DL */ |
| 1006 | |
| 1007 | |
| 1008 | #if !defined(NO_CACHING) |
| 1009 | static const char *month_names[] = {"Jan", |
| 1010 | "Feb", |
| 1011 | "Mar", |
| 1012 | "Apr", |
| 1013 | "May", |
| 1014 | "Jun", |
| 1015 | "Jul", |
| 1016 | "Aug", |
| 1017 | "Sep", |
| 1018 | "Oct", |
| 1019 | "Nov", |
| 1020 | "Dec"}; |
| 1021 | #endif /* !NO_CACHING */ |
| 1022 | |
| 1023 | /* Unified socket address. For IPv6 support, add IPv6 address structure in the |
| 1024 | * union u. */ |
| 1025 | union usa { |
| 1026 |  struct sockaddr sa; |
| 1027 |  struct sockaddr_in sin; |
| 1028 | #if defined(USE_IPV6) |
| 1029 |  struct sockaddr_in6 sin6; |
| 1030 | #endif |
| 1031 | }; |
| 1032 | |
| 1033 | /* Describes a string (chunk of memory). */ |
| 1034 | struct vec { |
| 1035 |  const char *ptr; |
| 1036 |  size_t len; |
| 1037 | }; |
| 1038 | |
| 1039 | struct file { |
| 1040 |  uint64_t size; |
| 1041 |  time_t last_modified; |
| 1042 |  FILE *fp; |
| 1043 |  const char *membuf; /* Non-NULL if file data is in memory */ |
| 1044 |  int is_directory; |
| 1045 |  int gzipped; /* set to 1 if the content is gzipped |
| 1046 |   * in which case we need a content-encoding: gzip header */ |
| 1047 | }; |
| 1048 | |
| 1049 | #define STRUCT_FILE_INITIALIZER \ |
| 1050 |  { \ |
| 1051 |    (uint64_t)0, (time_t)0, (FILE *)NULL, (const char *)NULL, 0, 0 \ |
| 1052 |  } |
| 1053 | |
| 1054 | /* Describes listening socket, or socket which was accept()-ed by the master |
| 1055 | * thread and queued for future handling by the worker thread. */ |
| 1056 | struct socket { |
| 1057 |  SOCKET sock; /* Listening socket */ |
| 1058 |  union usa lsa; /* Local socket address */ |
| 1059 |  union usa rsa; /* Remote socket address */ |
| 1060 |  unsigned char is_ssl; /* Is port SSL-ed */ |
| 1061 |  unsigned char ssl_redir; /* Is port supposed to redirect everything to SSL |
| 1062 |   * port */ |
| 1063 | }; |
| 1064 | |
| 1065 | /* NOTE(lsm): this enum shoulds be in sync with the config_options below. */ |
| 1066 | enum { |
| 1067 |  CGI_EXTENSIONS, |
| 1068 |  CGI_ENVIRONMENT, |
| 1069 |  PUT_DELETE_PASSWORDS_FILE, |
| 1070 |  CGI_INTERPRETER, |
| 1071 |  PROTECT_URI, |
| 1072 |  AUTHENTICATION_DOMAIN, |
| 1073 |  SSI_EXTENSIONS, |
| 1074 |  THROTTLE, |
| 1075 |  ACCESS_LOG_FILE, |
| 1076 |  ENABLE_DIRECTORY_LISTING, |
| 1077 |  ERROR_LOG_FILE, |
| 1078 |  GLOBAL_PASSWORDS_FILE, |
| 1079 |  INDEX_FILES, |
| 1080 |  ENABLE_KEEP_ALIVE, |
| 1081 |  ACCESS_CONTROL_LIST, |
| 1082 |  EXTRA_MIME_TYPES, |
| 1083 |  LISTENING_PORTS, |
| 1084 |  DOCUMENT_ROOT, |
| 1085 |  SSL_CERTIFICATE, |
| 1086 |  NUM_THREADS, |
| 1087 |  RUN_AS_USER, |
| 1088 |  REWRITE, |
| 1089 |  HIDE_FILES, |
| 1090 |  REQUEST_TIMEOUT, |
| 1091 |  SSL_DO_VERIFY_PEER, |
| 1092 |  SSL_CA_PATH, |
| 1093 |  SSL_CA_FILE, |
| 1094 |  SSL_VERIFY_DEPTH, |
| 1095 |  SSL_DEFAULT_VERIFY_PATHS, |
| 1096 |  SSL_CIPHER_LIST, |
| 1097 |  SSL_PROTOCOL_VERSION, |
| 1098 |  SSL_SHORT_TRUST, |
| 1099 | #if defined(USE_WEBSOCKET) |
| 1100 |  WEBSOCKET_TIMEOUT, |
| 1101 | #endif |
| 1102 |  DECODE_URL, |
| 1103 | |
| 1104 | #if defined(USE_LUA) |
| 1105 |  LUA_PRELOAD_FILE, |
| 1106 |  LUA_SCRIPT_EXTENSIONS, |
| 1107 |  LUA_SERVER_PAGE_EXTENSIONS, |
| 1108 | #endif |
| 1109 | #if defined(USE_DUKTAPE) |
| 1110 |  DUKTAPE_SCRIPT_EXTENSIONS, |
| 1111 | #endif |
| 1112 | |
| 1113 | #if defined(USE_WEBSOCKET) |
| 1114 |  WEBSOCKET_ROOT, |
| 1115 | #endif |
| 1116 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 1117 |  LUA_WEBSOCKET_EXTENSIONS, |
| 1118 | #endif |
| 1119 |  ACCESS_CONTROL_ALLOW_ORIGIN, |
| 1120 |  ERROR_PAGES, |
| 1121 |  CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the |
| 1122 | * socket option typedef TCP_NODELAY. */ |
| 1123 | #if !defined(NO_CACHING) |
| 1124 |  STATIC_FILE_MAX_AGE, |
| 1125 | #endif |
| 1126 | |
| 1127 |  NUM_OPTIONS |
| 1128 | }; |
| 1129 | |
| 1130 | |
| 1131 | /* Config option name, config types, default value */ |
| 1132 | static struct mg_option config_options[] = { |
| 1133 | {"cgi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.cgi$|**.pl$|**.php$"}, |
| 1134 | {"cgi_environment", CONFIG_TYPE_STRING, NULL}, |
| 1135 | {"put_delete_auth_file", CONFIG_TYPE_FILE, NULL}, |
| 1136 | {"cgi_interpreter", CONFIG_TYPE_FILE, NULL}, |
| 1137 | {"protect_uri", CONFIG_TYPE_STRING, NULL}, |
| 1138 | {"authentication_domain", CONFIG_TYPE_STRING, "mydomain.com"}, |
| 1139 | {"ssi_pattern", CONFIG_TYPE_EXT_PATTERN, "**.shtml$|**.shtm$"}, |
| 1140 | {"throttle", CONFIG_TYPE_STRING, NULL}, |
| 1141 | {"access_log_file", CONFIG_TYPE_FILE, NULL}, |
| 1142 | {"enable_directory_listing", CONFIG_TYPE_BOOLEAN, "yes"}, |
| 1143 | {"error_log_file", CONFIG_TYPE_FILE, NULL}, |
| 1144 | {"global_auth_file", CONFIG_TYPE_FILE, NULL}, |
| 1145 | {"index_files", |
| 1146 | CONFIG_TYPE_STRING, |
| 1147 | #ifdef USE_LUA |
| 1148 | "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi," |
| 1149 | "index.shtml,index.php"}, |
| 1150 | #else |
| 1151 | "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"}, |
| 1152 | #endif |
| 1153 | {"enable_keep_alive", CONFIG_TYPE_BOOLEAN, "no"}, |
| 1154 | {"access_control_list", CONFIG_TYPE_STRING, NULL}, |
| 1155 | {"extra_mime_types", CONFIG_TYPE_STRING, NULL}, |
| 1156 | {"listening_ports", CONFIG_TYPE_STRING, "8080"}, |
| 1157 | {"document_root", CONFIG_TYPE_DIRECTORY, NULL}, |
| 1158 | {"ssl_certificate", CONFIG_TYPE_FILE, NULL}, |
| 1159 | {"num_threads", CONFIG_TYPE_NUMBER, "50"}, |
| 1160 | {"run_as_user", CONFIG_TYPE_STRING, NULL}, |
| 1161 | {"url_rewrite_patterns", CONFIG_TYPE_STRING, NULL}, |
| 1162 | {"hide_files_patterns", CONFIG_TYPE_EXT_PATTERN, NULL}, |
| 1163 | {"request_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, |
| 1164 | {"ssl_verify_peer", CONFIG_TYPE_BOOLEAN, "no"}, |
| 1165 | {"ssl_ca_path", CONFIG_TYPE_DIRECTORY, NULL}, |
| 1166 | {"ssl_ca_file", CONFIG_TYPE_FILE, NULL}, |
| 1167 | {"ssl_verify_depth", CONFIG_TYPE_NUMBER, "9"}, |
| 1168 | {"ssl_default_verify_paths", CONFIG_TYPE_BOOLEAN, "yes"}, |
| 1169 | {"ssl_cipher_list", CONFIG_TYPE_STRING, NULL}, |
| 1170 | {"ssl_protocol_version", CONFIG_TYPE_NUMBER, "0"}, |
| 1171 | {"ssl_short_trust", CONFIG_TYPE_BOOLEAN, "no"}, |
| 1172 | #if defined(USE_WEBSOCKET) |
| 1173 | {"websocket_timeout_ms", CONFIG_TYPE_NUMBER, "30000"}, |
| 1174 | #endif |
| 1175 | {"decode_url", CONFIG_TYPE_BOOLEAN, "yes"}, |
| 1176 | |
| 1177 | #if defined(USE_LUA) |
| 1178 | {"lua_preload_file", CONFIG_TYPE_FILE, NULL}, |
| 1179 | {"lua_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, |
| 1180 | {"lua_server_page_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lp$|**.lsp$"}, |
| 1181 | #endif |
| 1182 | #if defined(USE_DUKTAPE) |
| 1183 | /* The support for duktape is still in alpha version state. |
| 1184 | * The name of this config option might change. */ |
| 1185 | {"duktape_script_pattern", CONFIG_TYPE_EXT_PATTERN, "**.ssjs$"}, |
| 1186 | #endif |
| 1187 | |
| 1188 | #if defined(USE_WEBSOCKET) |
| 1189 | {"websocket_root", CONFIG_TYPE_DIRECTORY, NULL}, |
| 1190 | #endif |
| 1191 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 1192 | {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"}, |
| 1193 | #endif |
| 1194 | {"access_control_allow_origin", CONFIG_TYPE_STRING, "*"}, |
| 1195 | {"error_pages", CONFIG_TYPE_DIRECTORY, NULL}, |
| 1196 | {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"}, |
| 1197 | #if !defined(NO_CACHING) |
| 1198 | {"static_file_max_age", CONFIG_TYPE_NUMBER, "3600"}, |
| 1199 | #endif |
| 1200 | |
| 1201 | {NULL, CONFIG_TYPE_UNKNOWN, NULL}}; |
| 1202 | |
| 1203 | /* Check if the config_options and the corresponding enum have compatible |
| 1204 | * sizes. */ |
| 1205 | mg_static_assert((sizeof(config_options) / sizeof(config_options[0])) |
| 1206 | == (NUM_OPTIONS + 1), |
| 1207 | "config_options and enum not sync"); |
| 1208 | |
| 1209 | enum { REQUEST_HANDLER, WEBSOCKET_HANDLER, AUTH_HANDLER }; |
| 1210 | |
| 1211 | struct mg_handler_info { |
| 1212 |  /* Name/Pattern of the URI. */ |
| 1213 |  char *uri; |
| 1214 |  size_t uri_len; |
| 1215 | |
| 1216 |  /* handler type */ |
| 1217 |  int handler_type; |
| 1218 | |
| 1219 |  /* Handler for http/https or authorization requests. */ |
| 1220 |  mg_request_handler handler; |
| 1221 | |
| 1222 |  /* Handler for ws/wss (websocket) requests. */ |
| 1223 |  mg_websocket_connect_handler connect_handler; |
| 1224 |  mg_websocket_ready_handler ready_handler; |
| 1225 |  mg_websocket_data_handler data_handler; |
| 1226 |  mg_websocket_close_handler close_handler; |
| 1227 | |
| 1228 |  /* Handler for authorization requests */ |
| 1229 |  mg_authorization_handler auth_handler; |
| 1230 | |
| 1231 |  /* User supplied argument for the handler function. */ |
| 1232 |  void *cbdata; |
| 1233 | |
| 1234 |  /* next handler in a linked list */ |
| 1235 |  struct mg_handler_info *next; |
| 1236 | }; |
| 1237 | |
| 1238 | struct mg_context { |
| 1239 |  volatile int stop_flag; /* Should we stop event loop */ |
| 1240 |  SSL_CTX *ssl_ctx; /* SSL context */ |
| 1241 |  char *config[NUM_OPTIONS]; /* Civetweb configuration parameters */ |
| 1242 |  struct mg_callbacks callbacks; /* User-defined callback function */ |
| 1243 |  void *user_data; /* User-defined data */ |
| 1244 |  int context_type; /* 1 = server context, 2 = client context */ |
| 1245 | |
| 1246 |  struct socket *listening_sockets; |
| 1247 |  in_port_t *listening_ports; |
| 1248 |  unsigned int num_listening_sockets; |
| 1249 | |
| 1250 |  volatile int |
| 1251 |   running_worker_threads; /* Number of currently running worker threads */ |
| 1252 |  pthread_mutex_t thread_mutex; /* Protects (max|num)_threads */ |
| 1253 |  pthread_cond_t thread_cond; /* Condvar for tracking workers terminations */ |
| 1254 | |
| 1255 |  struct socket queue[MGSQLEN]; /* Accepted sockets */ |
| 1256 |  volatile int sq_head; /* Head of the socket queue */ |
| 1257 |  volatile int sq_tail; /* Tail of the socket queue */ |
| 1258 |  pthread_cond_t sq_full; /* Signaled when socket is produced */ |
| 1259 |  pthread_cond_t sq_empty; /* Signaled when socket is consumed */ |
| 1260 |  pthread_t masterthreadid; /* The master thread ID */ |
| 1261 |  unsigned int |
| 1262 |   cfg_worker_threads; /* The number of configured worker threads. */ |
| 1263 |  pthread_t *workerthreadids; /* The worker thread IDs */ |
| 1264 | |
| 1265 |  time_t start_time; /* Server start time, used for authentication */ |
| 1266 |  uint64_t auth_nonce_mask; /* Mask for all nonce values */ |
| 1267 |  pthread_mutex_t nonce_mutex; /* Protects nonce_count */ |
| 1268 |  unsigned long nonce_count; /* Used nonces, used for authentication */ |
| 1269 | |
| 1270 |  char *systemName; /* What operating system is running */ |
| 1271 | |
| 1272 |  /* linked list of uri handlers */ |
| 1273 |  struct mg_handler_info *handlers; |
| 1274 | |
| 1275 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 1276 |  /* linked list of shared lua websockets */ |
| 1277 |  struct mg_shared_lua_websocket_list *shared_lua_websockets; |
| 1278 | #endif |
| 1279 | |
| 1280 | #ifdef USE_TIMERS |
| 1281 |  struct ttimers *timers; |
| 1282 | #endif |
| 1283 | }; |
| 1284 | |
| 1285 | |
| 1286 | struct mg_connection { |
| 1287 |  struct mg_request_info request_info; |
| 1288 |  struct mg_context *ctx; |
| 1289 |  SSL *ssl; /* SSL descriptor */ |
| 1290 |  SSL_CTX *client_ssl_ctx; /* SSL context for client connections */ |
| 1291 |  struct socket client; /* Connected client */ |
| 1292 |  time_t conn_birth_time; /* Time (wall clock) when connection was |
| 1293 |   * established */ |
| 1294 |  struct timespec req_time; /* Time (since system start) when the request |
| 1295 |   * was received */ |
| 1296 |  int64_t num_bytes_sent; /* Total bytes sent to client */ |
| 1297 |  int64_t content_len; /* Content-Length header value */ |
| 1298 |  int64_t consumed_content; /* How many bytes of content have been read */ |
| 1299 |  int is_chunked; /* Transfer-Encoding is chunked: 0=no, 1=yes: |
| 1300 |   * data available, 2: all data read */ |
| 1301 |  size_t chunk_remainder; /* Unread data from the last chunk */ |
| 1302 |  char *buf; /* Buffer for received data */ |
| 1303 |  char *path_info; /* PATH_INFO part of the URL */ |
| 1304 | |
| 1305 |  int must_close; /* 1 if connection must be closed */ |
| 1306 |  int in_error_handler; /* 1 if in handler for user defined error |
| 1307 |   * pages */ |
| 1308 |  int internal_error; /* 1 if an error occured while processing the |
| 1309 |   * request */ |
| 1310 | |
| 1311 |  int buf_size; /* Buffer size */ |
| 1312 |  int request_len; /* Size of the request + headers in a buffer */ |
| 1313 |  int data_len; /* Total size of data in a buffer */ |
| 1314 |  int status_code; /* HTTP reply status code, e.g. 200 */ |
| 1315 |  int throttle; /* Throttling, bytes/sec. <= 0 means no |
| 1316 |   * throttle */ |
| 1317 |  time_t last_throttle_time; /* Last time throttled data was sent */ |
| 1318 |  int64_t last_throttle_bytes; /* Bytes sent this second */ |
| 1319 |  pthread_mutex_t mutex; /* Used by mg_(un)lock_connection to ensure |
| 1320 |   * atomic transmissions for websockets */ |
| 1321 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 1322 |  void *lua_websocket_state; /* Lua_State for a websocket connection */ |
| 1323 | #endif |
| 1324 | }; |
| 1325 | |
| 1326 | |
| 1327 | static pthread_key_t sTlsKey; /* Thread local storage index */ |
| 1328 | static int sTlsInit = 0; |
| 1329 | static int thread_idx_max = 0; |
| 1330 | |
| 1331 | |
| 1332 | struct mg_workerTLS { |
| 1333 |  int is_master; |
| 1334 |  unsigned long thread_idx; |
| 1335 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 1336 |  HANDLE pthread_cond_helper_mutex; |
| 1337 | #endif |
| 1338 | }; |
| 1339 | |
| 1340 | /* Directory entry */ |
| 1341 | struct de { |
| 1342 |  struct mg_connection *conn; |
| 1343 |  char *file_name; |
| 1344 |  struct file file; |
| 1345 | }; |
| 1346 | |
| 1347 | |
| 1348 | #if defined(USE_WEBSOCKET) |
| 1349 | static int is_websocket_protocol(const struct mg_connection *conn); |
| 1350 | #else |
| 1351 | #define is_websocket_protocol(conn) (0) |
| 1352 | #endif |
| 1353 | |
| 1354 | |
| 1355 | static int |
| 1356 | mg_atomic_inc(volatile int *addr) |
| 1357 | { |
| 1358 |  int ret; |
| 1359 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 1360 |  /* Depending on the SDK, this function uses either |
| 1361 |   * (volatile unsigned int *) or (volatile LONG *), |
| 1362 |   * so whatever you use, the other SDK is likely to raise a warning. */ |
| 1363 |  ret = InterlockedIncrement((volatile long *)addr); |
| 1364 | #elif defined(__GNUC__) \ |
| 1365 | && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) |
| 1366 |  ret = __sync_add_and_fetch(addr, 1); |
| 1367 | #else |
| 1368 |  ret = (++(*addr)); |
| 1369 | #endif |
| 1370 |  return ret; |
| 1371 | } |
| 1372 | |
| 1373 | |
| 1374 | static int |
| 1375 | mg_atomic_dec(volatile int *addr) |
| 1376 | { |
| 1377 |  int ret; |
| 1378 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 1379 |  /* Depending on the SDK, this function uses either |
| 1380 |   * (volatile unsigned int *) or (volatile LONG *), |
| 1381 |   * so whatever you use, the other SDK is likely to raise a warning. */ |
| 1382 |  ret = InterlockedDecrement((volatile long *)addr); |
| 1383 | #elif defined(__GNUC__) \ |
| 1384 | && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0))) |
| 1385 |  ret = __sync_sub_and_fetch(addr, 1); |
| 1386 | #else |
| 1387 |  ret = (--(*addr)); |
| 1388 | #endif |
| 1389 |  return ret; |
| 1390 | } |
| 1391 | |
| 1392 | #if !defined(NO_THREAD_NAME) |
| 1393 | #if defined(_WIN32) && defined(_MSC_VER) |
| 1394 | /* Set the thread name for debugging purposes in Visual Studio |
| 1395 | * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx |
| 1396 | */ |
| 1397 | #pragma pack(push, 8) |
| 1398 | typedef struct tagTHREADNAME_INFO { |
| 1399 |  DWORD dwType; /* Must be 0x1000. */ |
| 1400 |  LPCSTR szName; /* Pointer to name (in user addr space). */ |
| 1401 |  DWORD dwThreadID; /* Thread ID (-1=caller thread). */ |
| 1402 |  DWORD dwFlags; /* Reserved for future use, must be zero. */ |
| 1403 | } THREADNAME_INFO; |
| 1404 | #pragma pack(pop) |
| 1405 | #elif defined(__linux__) |
| 1406 | #include <sys/prctl.h> |
| 1407 | #include <sys/sendfile.h> |
| 1408 | #endif |
| 1409 | |
| 1410 | |
| 1411 | static void |
| 1412 | mg_set_thread_name(const char *name) |
| 1413 | { |
| 1414 |  char threadName[16 + 1]; /* 16 = Max. thread length in Linux/OSX/.. */ |
| 1415 | |
| 1416 |  mg_snprintf( |
| 1417 |   NULL, NULL, threadName, sizeof(threadName), "civetweb-%s", name); |
| 1418 | |
| 1419 | #if defined(_WIN32) |
| 1420 | #if defined(_MSC_VER) |
| 1421 |  /* Windows and Visual Studio Compiler */ |
| 1422 |  __try |
| 1423 |  { |
| 1424 |    THREADNAME_INFO info; |
| 1425 |    info.dwType = 0x1000; |
| 1426 |    info.szName = threadName; |
| 1427 |    info.dwThreadID = ~0U; |
| 1428 |    info.dwFlags = 0; |
| 1429 | |
| 1430 |    RaiseException(0x406D1388, |
| 1431 |     0, |
| 1432 |     sizeof(info) / sizeof(ULONG_PTR), |
| 1433 |     (ULONG_PTR *)&info); |
| 1434 |  } |
| 1435 |  __except(EXCEPTION_EXECUTE_HANDLER) |
| 1436 |  { |
| 1437 |  } |
| 1438 | #elif defined(__MINGW32__) |
| 1439 | /* No option known to set thread name for MinGW */ |
| 1440 | #endif |
| 1441 | #elif defined(__GLIBC__) \ |
| 1442 | && ((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 12))) |
| 1443 |  /* pthread_setname_np first appeared in glibc in version 2.12*/ |
| 1444 |  (void)pthread_setname_np(pthread_self(), threadName); |
| 1445 | #elif defined(__linux__) |
| 1446 |  /* on linux we can use the old prctl function */ |
| 1447 |  (void)prctl(PR_SET_NAME, threadName, 0, 0, 0); |
| 1448 | #endif |
| 1449 | } |
| 1450 | #else /* !defined(NO_THREAD_NAME) */ |
| 1451 | void |
| 1452 | mg_set_thread_name(const char *threadName) |
| 1453 | { |
| 1454 | } |
| 1455 | #endif |
| 1456 | |
| 1457 | |
| 1458 | #if defined(MG_LEGACY_INTERFACE) |
| 1459 | const char ** |
| 1460 | mg_get_valid_option_names(void) |
| 1461 | { |
| 1462 |  /* This function is deprecated. Use mg_get_valid_options instead. */ |
| 1463 |  static const char * |
| 1464 |   data[2 * sizeof(config_options) / sizeof(config_options[0])] = {0}; |
| 1465 |  int i; |
| 1466 | |
| 1467 |  for (i = 0; config_options[i].name != NULL; i++) { |
| 1468 |    data[i * 2] = config_options[i].name; |
| 1469 |    data[i * 2 + 1] = config_options[i].default_value; |
| 1470 |  } |
| 1471 | |
| 1472 |  return data; |
| 1473 | } |
| 1474 | #endif |
| 1475 | |
| 1476 | |
| 1477 | const struct mg_option * |
| 1478 | mg_get_valid_options(void) |
| 1479 | { |
| 1480 |  return config_options; |
| 1481 | } |
| 1482 | |
| 1483 | |
| 1484 | static int |
| 1485 | is_file_in_memory(const struct mg_connection *conn, |
| 1486 | const char *path, |
| 1487 | struct file *filep) |
| 1488 | { |
| 1489 |  size_t size = 0; |
| 1490 |  if (!conn || !filep) { |
| 1491 |    return 0; |
| 1492 |  } |
| 1493 | |
| 1494 |  if (conn->ctx->callbacks.open_file) { |
| 1495 |    filep->membuf = conn->ctx->callbacks.open_file(conn, path, &size); |
| 1496 |    if (filep->membuf != NULL) { |
| 1497 |      /* NOTE: override filep->size only on success. Otherwise, it might |
| 1498 |       * break constructs like if (!mg_stat() || !mg_fopen()) ... */ |
| 1499 |      filep->size = size; |
| 1500 |    } |
| 1501 |  } |
| 1502 | |
| 1503 |  return filep->membuf != NULL; |
| 1504 | } |
| 1505 | |
| 1506 | |
| 1507 | static int |
| 1508 | is_file_opened(const struct file *filep) |
| 1509 | { |
| 1510 |  if (!filep) { |
| 1511 |    return 0; |
| 1512 |  } |
| 1513 | |
| 1514 |  return filep->membuf != NULL || filep->fp != NULL; |
| 1515 | } |
| 1516 | |
| 1517 | |
| 1518 | /* mg_fopen will open a file either in memory or on the disk. |
| 1519 | * The input parameter path is a string in UTF-8 encoding. |
| 1520 | * The input parameter mode is the same as for fopen. |
| 1521 | * Either fp or membuf will be set in the output struct filep. |
| 1522 | * The function returns 1 on success, 0 on error. */ |
| 1523 | static int |
| 1524 | mg_fopen(const struct mg_connection *conn, |
| 1525 | const char *path, |
| 1526 | const char *mode, |
| 1527 | struct file *filep) |
| 1528 | { |
| 1529 |  struct stat st; |
| 1530 | |
| 1531 |  if (!filep) { |
| 1532 |    return 0; |
| 1533 |  } |
| 1534 | |
| 1535 |  /* TODO (high): mg_fopen should only open a file, while mg_stat should |
| 1536 |   * only get the file status. They should not work on different members of |
| 1537 |   * the same structure (bad cohesion). */ |
| 1538 |  memset(filep, 0, sizeof(*filep)); |
| 1539 | |
| 1540 |  if (stat(path, &st) == 0) { |
| 1541 |    filep->size = (uint64_t)(st.st_size); |
| 1542 |  } |
| 1543 | |
| 1544 |  if (!is_file_in_memory(conn, path, filep)) { |
| 1545 | #ifdef _WIN32 |
| 1546 |    wchar_t wbuf[PATH_MAX], wmode[20]; |
| 1547 |    path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); |
| 1548 |    MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, ARRAY_SIZE(wmode)); |
| 1549 |    filep->fp = _wfopen(wbuf, wmode); |
| 1550 | #else |
| 1551 |    /* Linux et al already use unicode. No need to convert. */ |
| 1552 |    filep->fp = fopen(path, mode); |
| 1553 | #endif |
| 1554 |  } |
| 1555 | |
| 1556 |  return is_file_opened(filep); |
| 1557 | } |
| 1558 | |
| 1559 | |
| 1560 | static void |
| 1561 | mg_fclose(struct file *filep) |
| 1562 | { |
| 1563 |  if (filep != NULL && filep->fp != NULL) { |
| 1564 |    fclose(filep->fp); |
| 1565 |  } |
| 1566 | } |
| 1567 | |
| 1568 | |
| 1569 | static void |
| 1570 | mg_strlcpy(register char *dst, register const char *src, size_t n) |
| 1571 | { |
| 1572 |  for (; *src != '\0' && n > 1; n--) { |
| 1573 |    *dst++ = *src++; |
| 1574 |  } |
| 1575 |  *dst = '\0'; |
| 1576 | } |
| 1577 | |
| 1578 | |
| 1579 | static int |
| 1580 | lowercase(const char *s) |
| 1581 | { |
| 1582 |  return tolower(*(const unsigned char *)s); |
| 1583 | } |
| 1584 | |
| 1585 | |
| 1586 | int |
| 1587 | mg_strncasecmp(const char *s1, const char *s2, size_t len) |
| 1588 | { |
| 1589 |  int diff = 0; |
| 1590 | |
| 1591 |  if (len > 0) { |
| 1592 |    do { |
| 1593 |      diff = lowercase(s1++) - lowercase(s2++); |
| 1594 |    } while (diff == 0 && s1[-1] != '\0' && --len > 0); |
| 1595 |  } |
| 1596 | |
| 1597 |  return diff; |
| 1598 | } |
| 1599 | |
| 1600 | |
| 1601 | int |
| 1602 | mg_strcasecmp(const char *s1, const char *s2) |
| 1603 | { |
| 1604 |  int diff; |
| 1605 | |
| 1606 |  do { |
| 1607 |    diff = lowercase(s1++) - lowercase(s2++); |
| 1608 |  } while (diff == 0 && s1[-1] != '\0'); |
| 1609 | |
| 1610 |  return diff; |
| 1611 | } |
| 1612 | |
| 1613 | |
| 1614 | static char * |
| 1615 | mg_strndup(const char *ptr, size_t len) |
| 1616 | { |
| 1617 |  char *p; |
| 1618 | |
| 1619 |  if ((p = (char *)mg_malloc(len + 1)) != NULL) { |
| 1620 |    mg_strlcpy(p, ptr, len + 1); |
| 1621 |  } |
| 1622 | |
| 1623 |  return p; |
| 1624 | } |
| 1625 | |
| 1626 | |
| 1627 | static char * |
| 1628 | mg_strdup(const char *str) |
| 1629 | { |
| 1630 |  return mg_strndup(str, strlen(str)); |
| 1631 | } |
| 1632 | |
| 1633 | |
| 1634 | static const char * |
| 1635 | mg_strcasestr(const char *big_str, const char *small_str) |
| 1636 | { |
| 1637 |  size_t i, big_len = strlen(big_str), small_len = strlen(small_str); |
| 1638 | |
| 1639 |  if (big_len >= small_len) { |
| 1640 |    for (i = 0; i <= (big_len - small_len); i++) { |
| 1641 |      if (mg_strncasecmp(big_str + i, small_str, small_len) == 0) { |
| 1642 |        return big_str + i; |
| 1643 |      } |
| 1644 |    } |
| 1645 |  } |
| 1646 | |
| 1647 |  return NULL; |
| 1648 | } |
| 1649 | |
| 1650 | |
| 1651 | /* Return null terminated string of given maximum length. |
| 1652 | * Report errors if length is exceeded. */ |
| 1653 | static void |
| 1654 | mg_vsnprintf(const struct mg_connection *conn, |
| 1655 | int *truncated, |
| 1656 | char *buf, |
| 1657 | size_t buflen, |
| 1658 | const char *fmt, |
| 1659 | va_list ap) |
| 1660 | { |
| 1661 |  int n, ok; |
| 1662 | |
| 1663 |  if (buflen == 0) { |
| 1664 |    return; |
| 1665 |  } |
| 1666 | |
| 1667 | #ifdef __clang__ |
| 1668 | #pragma clang diagnostic push |
| 1669 | #pragma clang diagnostic ignored "-Wformat-nonliteral" |
| 1670 | /* Using fmt as a non-literal is intended here, since it is mostly called |
| 1671 | * indirectly by mg_snprintf */ |
| 1672 | #endif |
| 1673 | |
| 1674 |  n = (int)vsnprintf_impl(buf, buflen, fmt, ap); |
| 1675 |  ok = (n >= 0) && ((size_t)n < buflen); |
| 1676 | |
| 1677 | #ifdef __clang__ |
| 1678 | #pragma clang diagnostic pop |
| 1679 | #endif |
| 1680 | |
| 1681 |  if (ok) { |
| 1682 |    if (truncated) { |
| 1683 |      *truncated = 0; |
| 1684 |    } |
| 1685 |  } else { |
| 1686 |    if (truncated) { |
| 1687 |      *truncated = 1; |
| 1688 |    } |
| 1689 |    mg_cry(conn, |
| 1690 |     "truncating vsnprintf buffer: [%.*s]", |
| 1691 |     (int)((buflen > 200) ? 200 : (buflen - 1)), |
| 1692 |     buf); |
| 1693 |    n = (int)buflen - 1; |
| 1694 |  } |
| 1695 |  buf[n] = '\0'; |
| 1696 | } |
| 1697 | |
| 1698 | |
| 1699 | static void |
| 1700 | mg_snprintf(const struct mg_connection *conn, |
| 1701 | int *truncated, |
| 1702 | char *buf, |
| 1703 | size_t buflen, |
| 1704 | const char *fmt, |
| 1705 | ...) |
| 1706 | { |
| 1707 |  va_list ap; |
| 1708 | |
| 1709 |  va_start(ap, fmt); |
| 1710 |  mg_vsnprintf(conn, truncated, buf, buflen, fmt, ap); |
| 1711 |  va_end(ap); |
| 1712 | } |
| 1713 | |
| 1714 | |
| 1715 | static int |
| 1716 | get_option_index(const char *name) |
| 1717 | { |
| 1718 |  int i; |
| 1719 | |
| 1720 |  for (i = 0; config_options[i].name != NULL; i++) { |
| 1721 |    if (strcmp(config_options[i].name, name) == 0) { |
| 1722 |      return i; |
| 1723 |    } |
| 1724 |  } |
| 1725 |  return -1; |
| 1726 | } |
| 1727 | |
| 1728 | |
| 1729 | const char * |
| 1730 | mg_get_option(const struct mg_context *ctx, const char *name) |
| 1731 | { |
| 1732 |  int i; |
| 1733 |  if ((i = get_option_index(name)) == -1) { |
| 1734 |    return NULL; |
| 1735 |  } else if (!ctx || ctx->config[i] == NULL) { |
| 1736 |    return ""; |
| 1737 |  } else { |
| 1738 |    return ctx->config[i]; |
| 1739 |  } |
| 1740 | } |
| 1741 | |
| 1742 | |
| 1743 | struct mg_context * |
| 1744 | mg_get_context(const struct mg_connection *conn) |
| 1745 | { |
| 1746 |  return (conn == NULL) ? (struct mg_context *)NULL : (conn->ctx); |
| 1747 | } |
| 1748 | |
| 1749 | |
| 1750 | void * |
| 1751 | mg_get_user_data(const struct mg_context *ctx) |
| 1752 | { |
| 1753 |  return (ctx == NULL) ? NULL : ctx->user_data; |
| 1754 | } |
| 1755 | |
| 1756 | |
| 1757 | void |
| 1758 | mg_set_user_connection_data(struct mg_connection *conn, void *data) |
| 1759 | { |
| 1760 |  if (conn != NULL) { |
| 1761 |    conn->request_info.conn_data = data; |
| 1762 |  } |
| 1763 | } |
| 1764 | |
| 1765 | |
| 1766 | void * |
| 1767 | mg_get_user_connection_data(const struct mg_connection *conn) |
| 1768 | { |
| 1769 |  if (conn != NULL) { |
| 1770 |    return conn->request_info.conn_data; |
| 1771 |  } |
| 1772 |  return NULL; |
| 1773 | } |
| 1774 | |
| 1775 | |
| 1776 | size_t |
| 1777 | mg_get_ports(const struct mg_context *ctx, size_t size, int *ports, int *ssl) |
| 1778 | { |
| 1779 |  size_t i; |
| 1780 |  if (!ctx) { |
| 1781 |    return 0; |
| 1782 |  } |
| 1783 |  for (i = 0; i < size && i < ctx->num_listening_sockets; i++) { |
| 1784 |    ssl[i] = ctx->listening_sockets[i].is_ssl; |
| 1785 |    ports[i] = ctx->listening_ports[i]; |
| 1786 |  } |
| 1787 |  return i; |
| 1788 | } |
| 1789 | |
| 1790 | |
| 1791 | int |
| 1792 | mg_get_server_ports(const struct mg_context *ctx, |
| 1793 | int size, |
| 1794 | struct mg_server_ports *ports) |
| 1795 | { |
| 1796 |  int i, cnt = 0; |
| 1797 | |
| 1798 |  if (size <= 0) { |
| 1799 |    return -1; |
| 1800 |  } |
| 1801 |  memset(ports, 0, sizeof(*ports) * (size_t)size); |
| 1802 |  if (!ctx) { |
| 1803 |    return -1; |
| 1804 |  } |
| 1805 |  if (!ctx->listening_sockets || !ctx->listening_ports) { |
| 1806 |    return -1; |
| 1807 |  } |
| 1808 | |
| 1809 |  for (i = 0; (i < size) && (i < (int)ctx->num_listening_sockets); i++) { |
| 1810 | |
| 1811 |    ports[cnt].port = ctx->listening_ports[i]; |
| 1812 |    ports[cnt].is_ssl = ctx->listening_sockets[i].is_ssl; |
| 1813 |    ports[cnt].is_redirect = ctx->listening_sockets[i].ssl_redir; |
| 1814 | |
| 1815 |    if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET) { |
| 1816 |      /* IPv4 */ |
| 1817 |      ports[cnt].protocol = 1; |
| 1818 |      cnt++; |
| 1819 |    } else if (ctx->listening_sockets[i].lsa.sa.sa_family == AF_INET6) { |
| 1820 |      /* IPv6 */ |
| 1821 |      ports[cnt].protocol = 3; |
| 1822 |      cnt++; |
| 1823 |    } |
| 1824 |  } |
| 1825 | |
| 1826 |  return cnt; |
| 1827 | } |
| 1828 | |
| 1829 | |
| 1830 | static void |
| 1831 | sockaddr_to_string(char *buf, size_t len, const union usa *usa) |
| 1832 | { |
| 1833 |  buf[0] = '\0'; |
| 1834 | |
| 1835 |  if (!usa) { |
| 1836 |    return; |
| 1837 |  } |
| 1838 | |
| 1839 |  if (usa->sa.sa_family == AF_INET) { |
| 1840 |    getnameinfo(&usa->sa, |
| 1841 |     sizeof(usa->sin), |
| 1842 |     buf, |
| 1843 |     (unsigned)len, |
| 1844 |     NULL, |
| 1845 |     0, |
| 1846 |     NI_NUMERICHOST); |
| 1847 |  } |
| 1848 | #if defined(USE_IPV6) |
| 1849 |  else if (usa->sa.sa_family == AF_INET6) { |
| 1850 |    getnameinfo(&usa->sa, |
| 1851 |     sizeof(usa->sin6), |
| 1852 |     buf, |
| 1853 |     (unsigned)len, |
| 1854 |     NULL, |
| 1855 |     0, |
| 1856 |     NI_NUMERICHOST); |
| 1857 |  } |
| 1858 | #endif |
| 1859 | } |
| 1860 | |
| 1861 | |
| 1862 | /* Convert time_t to a string. According to RFC2616, Sec 14.18, this must be |
| 1863 | * included in all responses other than 100, 101, 5xx. */ |
| 1864 | static void |
| 1865 | gmt_time_string(char *buf, size_t buf_len, time_t *t) |
| 1866 | { |
| 1867 |  struct tm *tm; |
| 1868 | |
| 1869 |  tm = ((t != NULL) ? gmtime(t) : NULL); |
| 1870 |  if (tm != NULL) { |
| 1871 |    strftime(buf, buf_len, "%a, %d %b %Y %H:%M:%S GMT", tm); |
| 1872 |  } else { |
| 1873 |    mg_strlcpy(buf, "Thu, 01 Jan 1970 00:00:00 GMT", buf_len); |
| 1874 |    buf[buf_len - 1] = '\0'; |
| 1875 |  } |
| 1876 | } |
| 1877 | |
| 1878 | |
| 1879 | /* difftime for struct timespec. Return value is in seconds. */ |
| 1880 | static double |
| 1881 | mg_difftimespec(const struct timespec *ts_now, const struct timespec *ts_before) |
| 1882 | { |
| 1883 |  return (double)(ts_now->tv_nsec - ts_before->tv_nsec) * 1.0E-9 |
| 1884 |   + (double)(ts_now->tv_sec - ts_before->tv_sec); |
| 1885 | } |
| 1886 | |
| 1887 | |
| 1888 | /* Print error message to the opened error log stream. */ |
| 1889 | void |
| 1890 | mg_cry(const struct mg_connection *conn, const char *fmt, ...) |
| 1891 | { |
| 1892 |  char buf[MG_BUF_LEN], src_addr[IP_ADDR_STR_LEN]; |
| 1893 |  va_list ap; |
| 1894 |  struct file fi; |
| 1895 |  time_t timestamp; |
| 1896 | |
| 1897 |  va_start(ap, fmt); |
| 1898 |  IGNORE_UNUSED_RESULT(vsnprintf_impl(buf, sizeof(buf), fmt, ap)); |
| 1899 |  va_end(ap); |
| 1900 |  buf[sizeof(buf) - 1] = 0; |
| 1901 | |
| 1902 |  if (!conn) { |
| 1903 |    puts(buf); |
| 1904 |    return; |
| 1905 |  } |
| 1906 | |
| 1907 |  /* Do not lock when getting the callback value, here and below. |
| 1908 |   * I suppose this is fine, since function cannot disappear in the |
| 1909 |   * same way string option can. */ |
| 1910 |  if ((conn->ctx->callbacks.log_message == NULL) |
| 1911 |   || (conn->ctx->callbacks.log_message(conn, buf) == 0)) { |
| 1912 | |
| 1913 |    if (conn->ctx->config[ERROR_LOG_FILE] != NULL) { |
| 1914 |      if (mg_fopen(conn, conn->ctx->config[ERROR_LOG_FILE], "a+", &fi) |
| 1915 |       == 0) { |
| 1916 |        fi.fp = NULL; |
| 1917 |      } |
| 1918 |    } else { |
| 1919 |      fi.fp = NULL; |
| 1920 |    } |
| 1921 | |
| 1922 |    if (fi.fp != NULL) { |
| 1923 |      flockfile(fi.fp); |
| 1924 |      timestamp = time(NULL); |
| 1925 | |
| 1926 |      sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); |
| 1927 |      fprintf(fi.fp, |
| 1928 |       "[%010lu] [error] [client %s] ", |
| 1929 |       (unsigned long)timestamp, |
| 1930 |       src_addr); |
| 1931 | |
| 1932 |      if (conn->request_info.request_method != NULL) { |
| 1933 |        fprintf(fi.fp, |
| 1934 |         "%s %s: ", |
| 1935 |         conn->request_info.request_method, |
| 1936 |         conn->request_info.request_uri); |
| 1937 |      } |
| 1938 | |
| 1939 |      fprintf(fi.fp, "%s", buf); |
| 1940 |      fputc('\n', fi.fp); |
| 1941 |      fflush(fi.fp); |
| 1942 |      funlockfile(fi.fp); |
| 1943 |      mg_fclose(&fi); |
| 1944 |    } |
| 1945 |  } |
| 1946 | } |
| 1947 | |
| 1948 | |
| 1949 | /* Return fake connection structure. Used for logging, if connection |
| 1950 | * is not applicable at the moment of logging. */ |
| 1951 | static struct mg_connection * |
| 1952 | fc(struct mg_context *ctx) |
| 1953 | { |
| 1954 |  static struct mg_connection fake_connection; |
| 1955 |  fake_connection.ctx = ctx; |
| 1956 |  return &fake_connection; |
| 1957 | } |
| 1958 | |
| 1959 | |
| 1960 | const char * |
| 1961 | mg_version(void) |
| 1962 | { |
| 1963 |  return CIVETWEB_VERSION; |
| 1964 | } |
| 1965 | |
| 1966 | |
| 1967 | const struct mg_request_info * |
| 1968 | mg_get_request_info(const struct mg_connection *conn) |
| 1969 | { |
| 1970 |  if (!conn) { |
| 1971 |    return NULL; |
| 1972 |  } |
| 1973 |  return &conn->request_info; |
| 1974 | } |
| 1975 | |
| 1976 | |
| 1977 | /* Skip the characters until one of the delimiters characters found. |
| 1978 | * 0-terminate resulting word. Skip the delimiter and following whitespaces. |
| 1979 | * Advance pointer to buffer to the next word. Return found 0-terminated word. |
| 1980 | * Delimiters can be quoted with quotechar. */ |
| 1981 | static char * |
| 1982 | skip_quoted(char **buf, |
| 1983 | const char *delimiters, |
| 1984 | const char *whitespace, |
| 1985 | char quotechar) |
| 1986 | { |
| 1987 |  char *p, *begin_word, *end_word, *end_whitespace; |
| 1988 | |
| 1989 |  begin_word = *buf; |
| 1990 |  end_word = begin_word + strcspn(begin_word, delimiters); |
| 1991 | |
| 1992 |  /* Check for quotechar */ |
| 1993 |  if (end_word > begin_word) { |
| 1994 |    p = end_word - 1; |
| 1995 |    while (*p == quotechar) { |
| 1996 |      /* While the delimiter is quoted, look for the next delimiter. */ |
| 1997 |      /* This happens, e.g., in calls from parse_auth_header, |
| 1998 |       * if the user name contains a " character. */ |
| 1999 | |
| 2000 |      /* If there is anything beyond end_word, copy it. */ |
| 2001 |      if (*end_word != '\0') { |
| 2002 |        size_t end_off = strcspn(end_word + 1, delimiters); |
| 2003 |        memmove(p, end_word, end_off + 1); |
| 2004 |        p += end_off; /* p must correspond to end_word - 1 */ |
| 2005 |        end_word += end_off + 1; |
| 2006 |      } else { |
| 2007 |        *p = '\0'; |
| 2008 |        break; |
| 2009 |      } |
| 2010 |    } |
| 2011 |    for (p++; p < end_word; p++) { |
| 2012 |      *p = '\0'; |
| 2013 |    } |
| 2014 |  } |
| 2015 | |
| 2016 |  if (*end_word == '\0') { |
| 2017 |    *buf = end_word; |
| 2018 |  } else { |
| 2019 |    end_whitespace = end_word + 1 + strspn(end_word + 1, whitespace); |
| 2020 | |
| 2021 |    for (p = end_word; p < end_whitespace; p++) { |
| 2022 |      *p = '\0'; |
| 2023 |    } |
| 2024 | |
| 2025 |    *buf = end_whitespace; |
| 2026 |  } |
| 2027 | |
| 2028 |  return begin_word; |
| 2029 | } |
| 2030 | |
| 2031 | |
| 2032 | /* Simplified version of skip_quoted without quote char |
| 2033 | * and whitespace == delimiters */ |
| 2034 | static char * |
| 2035 | skip(char **buf, const char *delimiters) |
| 2036 | { |
| 2037 |  return skip_quoted(buf, delimiters, delimiters, 0); |
| 2038 | } |
| 2039 | |
| 2040 | |
| 2041 | /* Return HTTP header value, or NULL if not found. */ |
| 2042 | static const char * |
| 2043 | get_header(const struct mg_request_info *ri, const char *name) |
| 2044 | { |
| 2045 |  int i; |
| 2046 |  if (ri) { |
| 2047 |    for (i = 0; i < ri->num_headers; i++) { |
| 2048 |      if (!mg_strcasecmp(name, ri->http_headers[i].name)) { |
| 2049 |        return ri->http_headers[i].value; |
| 2050 |      } |
| 2051 |    } |
| 2052 |  } |
| 2053 | |
| 2054 |  return NULL; |
| 2055 | } |
| 2056 | |
| 2057 | |
| 2058 | const char * |
| 2059 | mg_get_header(const struct mg_connection *conn, const char *name) |
| 2060 | { |
| 2061 |  if (!conn) { |
| 2062 |    return NULL; |
| 2063 |  } |
| 2064 | |
| 2065 |  return get_header(&conn->request_info, name); |
| 2066 | } |
| 2067 | |
| 2068 | |
| 2069 | /* A helper function for traversing a comma separated list of values. |
| 2070 | * It returns a list pointer shifted to the next value, or NULL if the end |
| 2071 | * of the list found. |
| 2072 | * Value is stored in val vector. If value has form "x=y", then eq_val |
| 2073 | * vector is initialized to point to the "y" part, and val vector length |
| 2074 | * is adjusted to point only to "x". */ |
| 2075 | static const char * |
| 2076 | next_option(const char *list, struct vec *val, struct vec *eq_val) |
| 2077 | { |
| 2078 |  int end; |
| 2079 | |
| 2080 | reparse: |
| 2081 |  if (val == NULL || list == NULL || *list == '\0') { |
| 2082 |    /* End of the list */ |
| 2083 |    list = NULL; |
| 2084 |  } else { |
| 2085 |    /* Skip over leading LWS */ |
| 2086 |    while (*list == ' ' || *list == '\t') |
| 2087 |      list++; |
| 2088 | |
| 2089 |    val->ptr = list; |
| 2090 |    if ((list = strchr(val->ptr, ',')) != NULL) { |
| 2091 |      /* Comma found. Store length and shift the list ptr */ |
| 2092 |      val->len = ((size_t)(list - val->ptr)); |
| 2093 |      list++; |
| 2094 |    } else { |
| 2095 |      /* This value is the last one */ |
| 2096 |      list = val->ptr + strlen(val->ptr); |
| 2097 |      val->len = ((size_t)(list - val->ptr)); |
| 2098 |    } |
| 2099 | |
| 2100 |    /* Adjust length for trailing LWS */ |
| 2101 |    end = (int)val->len - 1; |
| 2102 |    while (end >= 0 && (val->ptr[end] == ' ' || val->ptr[end] == '\t')) |
| 2103 |      end--; |
| 2104 |    val->len = (size_t)(end + 1); |
| 2105 | |
| 2106 |    if (val->len == 0) { |
| 2107 |      /* Ignore any empty entries. */ |
| 2108 |      goto reparse; |
| 2109 |    } |
| 2110 | |
| 2111 |    if (eq_val != NULL) { |
| 2112 |      /* Value has form "x=y", adjust pointers and lengths |
| 2113 |       * so that val points to "x", and eq_val points to "y". */ |
| 2114 |      eq_val->len = 0; |
| 2115 |      eq_val->ptr = (const char *)memchr(val->ptr, '=', val->len); |
| 2116 |      if (eq_val->ptr != NULL) { |
| 2117 |        eq_val->ptr++; /* Skip over '=' character */ |
| 2118 |        eq_val->len = ((size_t)(val->ptr - eq_val->ptr)) + val->len; |
| 2119 |        val->len = ((size_t)(eq_val->ptr - val->ptr)) - 1; |
| 2120 |      } |
| 2121 |    } |
| 2122 |  } |
| 2123 | |
| 2124 |  return list; |
| 2125 | } |
| 2126 | |
| 2127 | /* A helper function for checking if a comma separated list of values contains |
| 2128 | * the given option (case insensitvely). |
| 2129 | * 'header' can be NULL, in which case false is returned. */ |
| 2130 | static int |
| 2131 | header_has_option(const char *header, const char *option) |
| 2132 | { |
| 2133 |  struct vec opt_vec; |
| 2134 |  struct vec eq_vec; |
| 2135 | |
| 2136 |  assert(option != NULL); |
| 2137 |  assert(option[0] != '\0'); |
| 2138 | |
| 2139 |  while ((header = next_option(header, &opt_vec, &eq_vec)) != NULL) { |
| 2140 |    if (mg_strncasecmp(option, opt_vec.ptr, opt_vec.len) == 0) |
| 2141 |      return 1; |
| 2142 |  } |
| 2143 | |
| 2144 |  return 0; |
| 2145 | } |
| 2146 | |
| 2147 | /* Perform case-insensitive match of string against pattern */ |
| 2148 | static int |
| 2149 | match_prefix(const char *pattern, size_t pattern_len, const char *str) |
| 2150 | { |
| 2151 |  const char *or_str; |
| 2152 |  size_t i; |
| 2153 |  int j, len, res; |
| 2154 | |
| 2155 |  if ((or_str = (const char *)memchr(pattern, '|', pattern_len)) != NULL) { |
| 2156 |    res = match_prefix(pattern, (size_t)(or_str - pattern), str); |
| 2157 |    return res > 0 ? res : match_prefix(or_str + 1, |
| 2158 |     (size_t)((pattern + pattern_len) |
| 2159 |     - (or_str + 1)), |
| 2160 |     str); |
| 2161 |  } |
| 2162 | |
| 2163 |  for (i = 0, j = 0; i < pattern_len; i++, j++) { |
| 2164 |    if (pattern[i] == '?' && str[j] != '\0') { |
| 2165 |      continue; |
| 2166 |    } else if (pattern[i] == '$') { |
| 2167 |      return str[j] == '\0' ? j : -1; |
| 2168 |    } else if (pattern[i] == '*') { |
| 2169 |      i++; |
| 2170 |      if (pattern[i] == '*') { |
| 2171 |        i++; |
| 2172 |        len = (int)strlen(str + j); |
| 2173 |      } else { |
| 2174 |        len = (int)strcspn(str + j, "/"); |
| 2175 |      } |
| 2176 |      if (i == pattern_len) { |
| 2177 |        return j + len; |
| 2178 |      } |
| 2179 |      do { |
| 2180 |        res = match_prefix(pattern + i, pattern_len - i, str + j + len); |
| 2181 |      } while (res == -1 && len-- > 0); |
| 2182 |      return res == -1 ? -1 : j + res + len; |
| 2183 |    } else if (lowercase(&pattern[i]) != lowercase(&str[j])) { |
| 2184 |      return -1; |
| 2185 |    } |
| 2186 |  } |
| 2187 |  return j; |
| 2188 | } |
| 2189 | |
| 2190 | |
| 2191 | /* HTTP 1.1 assumes keep alive if "Connection:" header is not set |
| 2192 | * This function must tolerate situations when connection info is not |
| 2193 | * set up, for example if request parsing failed. */ |
| 2194 | static int |
| 2195 | should_keep_alive(const struct mg_connection *conn) |
| 2196 | { |
| 2197 |  if (conn != NULL) { |
| 2198 |    const char *http_version = conn->request_info.http_version; |
| 2199 |    const char *header = mg_get_header(conn, "Connection"); |
| 2200 |    if (conn->must_close || conn->internal_error || conn->status_code == 401 |
| 2201 |     || mg_strcasecmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes") != 0 |
| 2202 |     || (header != NULL && !header_has_option(header, "keep-alive")) |
| 2203 |     || ( |
| 2204 |     )) { |
| 2205 |      return 0; |
| 2206 |    } |
| 2207 |    return 1; |
| 2208 |  } |
| 2209 |  return 0; |
| 2210 | } |
| 2211 | |
| 2212 | |
| 2213 | static int |
| 2214 | should_decode_url(const struct mg_connection *conn) |
| 2215 | { |
| 2216 |  if (!conn || !conn->ctx) { |
| 2217 |    return 0; |
| 2218 |  } |
| 2219 | |
| 2220 |  return (mg_strcasecmp(conn->ctx->config[DECODE_URL], "yes") == 0); |
| 2221 | } |
| 2222 | |
| 2223 | |
| 2224 | static const char * |
| 2225 | suggest_connection_header(const struct mg_connection *conn) |
| 2226 | { |
| 2227 |  return should_keep_alive(conn) ? "keep-alive" : "close"; |
| 2228 | } |
| 2229 | |
| 2230 | |
| 2231 | static int |
| 2232 | send_no_cache_header(struct mg_connection *conn) |
| 2233 | { |
| 2234 |  /* Send all current and obsolete cache opt-out directives. */ |
| 2235 |  return mg_printf(conn, |
| 2236 |   "Cache-Control: no-cache, no-store, " |
| 2237 |   "must-revalidate, private, max-age=0\r\n" |
| 2238 |   "Pragma: no-cache\r\n" |
| 2239 |   "Expires: 0\r\n"); |
| 2240 | } |
| 2241 | |
| 2242 | |
| 2243 | static int |
| 2244 | send_static_cache_header(struct mg_connection *conn) |
| 2245 | { |
| 2246 | #if !defined(NO_CACHING) |
| 2247 |  /* Read the server config to check how long a file may be cached. |
| 2248 |   * The configuration is in seconds. */ |
| 2249 |  int max_age = atoi(conn->ctx->config[STATIC_FILE_MAX_AGE]); |
| 2250 |  if (max_age <= 0) { |
| 2251 |    /* 0 means "do not cache". All values <0 are reserved |
| 2252 |     * and may be used differently in the future. */ |
| 2253 |    /* If a file should not be cached, do not only send |
| 2254 |     * max-age=0, but also pragmas and Expires headers. */ |
| 2255 |    return send_no_cache_header(conn); |
| 2256 |  } |
| 2257 | |
| 2258 |  /* Use "Cache-Control: max-age" instead of "Expires" header. |
| 2259 |   * Reason: see https://www.mnot.net/blog/2007/05/15/expires_max-age */ |
| 2260 |  /* See also https://www.mnot.net/cache_docs/ */ |
| 2261 |  /* According to RFC 2616, Section 14.21, caching times should not exceed |
| 2262 |   * one year. A year with 365 days corresponds to 31536000 seconds, a leap |
| 2263 |   * year to 31622400 seconds. For the moment, we just send whatever has |
| 2264 |   * been configured, still the behavior for >1 year should be considered |
| 2265 |   * as undefined. */ |
| 2266 |  return mg_printf(conn, "Cache-Control: max-age=%u\r\n", (unsigned)max_age); |
| 2267 | #else /* NO_CACHING */ |
| 2268 |  return send_no_cache_header(conn); |
| 2269 | #endif /* !NO_CACHING */ |
| 2270 | } |
| 2271 | |
| 2272 | |
| 2273 | static void handle_file_based_request(struct mg_connection *conn, |
| 2274 | const char *path, |
| 2275 | struct file *filep); |
| 2276 | |
| 2277 | static int |
| 2278 | mg_stat(struct mg_connection *conn, const char *path, struct file *filep); |
| 2279 | |
| 2280 | |
| 2281 | const char * |
| 2282 | mg_get_response_code_text(struct mg_connection *conn, int response_code) |
| 2283 | { |
| 2284 |  /* See IANA HTTP status code assignment: |
| 2285 |   * http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
| 2286 |   */ |
| 2287 | |
| 2288 |  switch (response_code) { |
| 2289 |  /* RFC2616 Section 10.1 - Informational 1xx */ |
| 2290 |  case 100: |
| 2291 |    return "Continue"; /* RFC2616 Section 10.1.1 */ |
| 2292 |  case 101: |
| 2293 |    return "Switching Protocols"; /* RFC2616 Section 10.1.2 */ |
| 2294 |  case 102: |
| 2295 |    return "Processing"; /* RFC2518 Section 10.1 */ |
| 2296 | |
| 2297 |  /* RFC2616 Section 10.2 - Successful 2xx */ |
| 2298 |  case 200: |
| 2299 |    return "OK"; /* RFC2616 Section 10.2.1 */ |
| 2300 |  case 201: |
| 2301 |    return "Created"; /* RFC2616 Section 10.2.2 */ |
| 2302 |  case 202: |
| 2303 |    return "Accepted"; /* RFC2616 Section 10.2.3 */ |
| 2304 |  case 203: |
| 2305 |    return "Non-Authoritative Information"; /* RFC2616 Section 10.2.4 */ |
| 2306 |  case 204: |
| 2307 |    return "No Content"; /* RFC2616 Section 10.2.5 */ |
| 2308 |  case 205: |
| 2309 |    return "Reset Content"; /* RFC2616 Section 10.2.6 */ |
| 2310 |  case 206: |
| 2311 |    return "Partial Content"; /* RFC2616 Section 10.2.7 */ |
| 2312 |  case 207: |
| 2313 |    return "Multi-Status"; /* RFC2518 Section 10.2, RFC4918 Section 11.1 */ |
| 2314 |  case 208: |
| 2315 |    return "Already Reported"; /* RFC5842 Section 7.1 */ |
| 2316 | |
| 2317 |  case 226: |
| 2318 |    return "IM used"; /* RFC3229 Section 10.4.1 */ |
| 2319 | |
| 2320 |  /* RFC2616 Section 10.3 - Redirection 3xx */ |
| 2321 |  case 300: |
| 2322 |    return "Multiple Choices"; /* RFC2616 Section 10.3.1 */ |
| 2323 |  case 301: |
| 2324 |    return "Moved Permanently"; /* RFC2616 Section 10.3.2 */ |
| 2325 |  case 302: |
| 2326 |    return "Found"; /* RFC2616 Section 10.3.3 */ |
| 2327 |  case 303: |
| 2328 |    return "See Other"; /* RFC2616 Section 10.3.4 */ |
| 2329 |  case 304: |
| 2330 |    return "Not Modified"; /* RFC2616 Section 10.3.5 */ |
| 2331 |  case 305: |
| 2332 |    return "Use Proxy"; /* RFC2616 Section 10.3.6 */ |
| 2333 |  case 307: |
| 2334 |    return "Temporary Redirect"; /* RFC2616 Section 10.3.8 */ |
| 2335 |  case 308: |
| 2336 |    return "Permanent Redirect"; /* RFC7238 Section 3 */ |
| 2337 | |
| 2338 |  /* RFC2616 Section 10.4 - Client Error 4xx */ |
| 2339 |  case 400: |
| 2340 |    return "Bad Request"; /* RFC2616 Section 10.4.1 */ |
| 2341 |  case 401: |
| 2342 |    return "Unauthorized"; /* RFC2616 Section 10.4.2 */ |
| 2343 |  case 402: |
| 2344 |    return "Payment Required"; /* RFC2616 Section 10.4.3 */ |
| 2345 |  case 403: |
| 2346 |    return "Forbidden"; /* RFC2616 Section 10.4.4 */ |
| 2347 |  case 404: |
| 2348 |    return "Not Found"; /* RFC2616 Section 10.4.5 */ |
| 2349 |  case 405: |
| 2350 |    return "Method Not Allowed"; /* RFC2616 Section 10.4.6 */ |
| 2351 |  case 406: |
| 2352 |    return "Not Acceptable"; /* RFC2616 Section 10.4.7 */ |
| 2353 |  case 407: |
| 2354 |    return "Proxy Authentication Required"; /* RFC2616 Section 10.4.8 */ |
| 2355 |  case 408: |
| 2356 |    return "Request Time-out"; /* RFC2616 Section 10.4.9 */ |
| 2357 |  case 409: |
| 2358 |    return "Conflict"; /* RFC2616 Section 10.4.10 */ |
| 2359 |  case 410: |
| 2360 |    return "Gone"; /* RFC2616 Section 10.4.11 */ |
| 2361 |  case 411: |
| 2362 |    return "Length Required"; /* RFC2616 Section 10.4.12 */ |
| 2363 |  case 412: |
| 2364 |    return "Precondition Failed"; /* RFC2616 Section 10.4.13 */ |
| 2365 |  case 413: |
| 2366 |    return "Request Entity Too Large"; /* RFC2616 Section 10.4.14 */ |
| 2367 |  case 414: |
| 2368 |    return "Request-URI Too Large"; /* RFC2616 Section 10.4.15 */ |
| 2369 |  case 415: |
| 2370 |    return "Unsupported Media Type"; /* RFC2616 Section 10.4.16 */ |
| 2371 |  case 416: |
| 2372 |    return "Requested range not satisfiable"; /* RFC2616 Section 10.4.17 */ |
| 2373 |  case 417: |
| 2374 |    return "Expectation Failed"; /* RFC2616 Section 10.4.18 */ |
| 2375 | |
| 2376 |  case 421: |
| 2377 |    return "Misdirected Request"; /* RFC7540 Section 9.1.2 */ |
| 2378 |  case 422: |
| 2379 |    return "Unproccessable entity"; /* RFC2518 Section 10.3, RFC4918 |
| 2380 |     * Section 11.2 */ |
| 2381 |  case 423: |
| 2382 |    return "Locked"; /* RFC2518 Section 10.4, RFC4918 Section 11.3 */ |
| 2383 |  case 424: |
| 2384 |    return "Failed Dependency"; /* RFC2518 Section 10.5, RFC4918 |
| 2385 |     * Section 11.4 */ |
| 2386 | |
| 2387 |  case 426: |
| 2388 |    return "Upgrade Required"; /* RFC 2817 Section 4 */ |
| 2389 | |
| 2390 |  case 428: |
| 2391 |    return "Precondition Required"; /* RFC 6585, Section 3 */ |
| 2392 |  case 429: |
| 2393 |    return "Too Many Requests"; /* RFC 6585, Section 4 */ |
| 2394 | |
| 2395 |  case 431: |
| 2396 |    return "Request Header Fields Too Large"; /* RFC 6585, Section 5 */ |
| 2397 | |
| 2398 |  case 451: |
| 2399 |    return "Unavailable For Legal Reasons"; /* draft-tbray-http-legally-restricted-status-05, |
| 2400 |     * Section 3 */ |
| 2401 | |
| 2402 |  /* RFC2616 Section 10.5 - Server Error 5xx */ |
| 2403 |  case 500: |
| 2404 |    return "Internal Server Error"; /* RFC2616 Section 10.5.1 */ |
| 2405 |  case 501: |
| 2406 |    return "Not Implemented"; /* RFC2616 Section 10.5.2 */ |
| 2407 |  case 502: |
| 2408 |    return "Bad Gateway"; /* RFC2616 Section 10.5.3 */ |
| 2409 |  case 503: |
| 2410 |    return "Service Unavailable"; /* RFC2616 Section 10.5.4 */ |
| 2411 |  case 504: |
| 2412 |    return "Gateway Time-out"; /* RFC2616 Section 10.5.5 */ |
| 2413 |  case 505: |
| 2414 |    return "HTTP Version not supported"; /* RFC2616 Section 10.5.6 */ |
| 2415 |  case 506: |
| 2416 |    return "Variant Also Negotiates"; /* RFC 2295, Section 8.1 */ |
| 2417 |  case 507: |
| 2418 |    return "Insufficient Storage"; /* RFC2518 Section 10.6, RFC4918 |
| 2419 |     * Section 11.5 */ |
| 2420 |  case 508: |
| 2421 |    return "Loop Detected"; /* RFC5842 Section 7.1 */ |
| 2422 | |
| 2423 |  case 510: |
| 2424 |    return "Not Extended"; /* RFC 2774, Section 7 */ |
| 2425 |  case 511: |
| 2426 |    return "Network Authentication Required"; /* RFC 6585, Section 6 */ |
| 2427 | |
| 2428 |  /* Other status codes, not shown in the IANA HTTP status code assignment. |
| 2429 |   * E.g., "de facto" standards due to common use, ... */ |
| 2430 |  case 418: |
| 2431 |    return "I am a teapot"; /* RFC2324 Section 2.3.2 */ |
| 2432 |  case 419: |
| 2433 |    return "Authentication Timeout"; /* common use */ |
| 2434 |  case 420: |
| 2435 |    return "Enhance Your Calm"; /* common use */ |
| 2436 |  case 440: |
| 2437 |    return "Login Timeout"; /* common use */ |
| 2438 |  case 509: |
| 2439 |    return "Bandwidth Limit Exceeded"; /* common use */ |
| 2440 | |
| 2441 |  default: |
| 2442 |    /* This error code is unknown. This should not happen. */ |
| 2443 |    if (conn) { |
| 2444 |      mg_cry(conn, "Unknown HTTP response code: %u", response_code); |
| 2445 |    } |
| 2446 | |
| 2447 |    /* Return at least a category according to RFC 2616 Section 10. */ |
| 2448 |    if (response_code >= 100 && response_code < 200) { |
| 2449 |      /* Unknown informational status code */ |
| 2450 |      return "Information"; |
| 2451 |    } |
| 2452 |    if (response_code >= 200 && response_code < 300) { |
| 2453 |      /* Unknown success code */ |
| 2454 |      return "Success"; |
| 2455 |    } |
| 2456 |    if (response_code >= 300 && response_code < 400) { |
| 2457 |      /* Unknown redirection code */ |
| 2458 |      return "Redirection"; |
| 2459 |    } |
| 2460 |    if (response_code >= 400 && response_code < 500) { |
| 2461 |      /* Unknown request error code */ |
| 2462 |      return "Client Error"; |
| 2463 |    } |
| 2464 |    if (response_code >= 500 && response_code < 600) { |
| 2465 |      /* Unknown server error code */ |
| 2466 |      return "Server Error"; |
| 2467 |    } |
| 2468 | |
| 2469 |    /* Response code not even within reasonable range */ |
| 2470 |    return ""; |
| 2471 |  } |
| 2472 | } |
| 2473 | |
| 2474 | |
| 2475 | static void send_http_error(struct mg_connection *, |
| 2476 | int, |
| 2477 | PRINTF_FORMAT_STRING(const char *fmt), |
| 2478 | ...) PRINTF_ARGS(3, 4); |
| 2479 | |
| 2480 | static void |
| 2481 | send_http_error(struct mg_connection *conn, int status, const char *fmt, ...) |
| 2482 | { |
| 2483 |  char buf[MG_BUF_LEN]; |
| 2484 |  va_list ap; |
| 2485 |  int len, i, page_handler_found, scope, truncated; |
| 2486 |  char date[64]; |
| 2487 |  time_t curtime = time(NULL); |
| 2488 |  const char *error_handler = NULL; |
| 2489 |  struct file error_page_file = STRUCT_FILE_INITIALIZER; |
| 2490 |  const char *error_page_file_ext, *tstr; |
| 2491 | |
| 2492 |  const char *status_text = mg_get_response_code_text(conn, status); |
| 2493 | |
| 2494 |  if (conn == NULL) { |
| 2495 |    return; |
| 2496 |  } |
| 2497 | |
| 2498 |  conn->status_code = status; |
| 2499 |  if ( |
| 2500 |   ) { |
| 2501 |    if (!conn->in_error_handler) { |
| 2502 |      /* Send user defined error pages, if defined */ |
| 2503 |      error_handler = conn->ctx->config[ERROR_PAGES]; |
| 2504 |      error_page_file_ext = conn->ctx->config[INDEX_FILES]; |
| 2505 |      page_handler_found = 0; |
| 2506 |      if (error_handler != NULL) { |
| 2507 |        for (scope = 1; (scope <= 3) && !page_handler_found; scope++) { |
| 2508 |          switch (scope) { |
| 2509 |          case 1: /* Handler for specific error, e.g. 404 error */ |
| 2510 |            mg_snprintf(conn, |
| 2511 |             &truncated, |
| 2512 |             buf, |
| 2513 |             sizeof(buf) - 32, |
| 2514 |             "%serror%03u.", |
| 2515 |             error_handler, |
| 2516 |             status); |
| 2517 |            break; |
| 2518 |          case 2: /* Handler for error group, e.g., 5xx error handler |
| 2519 |           * for all server errors (500-599) */ |
| 2520 |            mg_snprintf(conn, |
| 2521 |             &truncated, |
| 2522 |             buf, |
| 2523 |             sizeof(buf) - 32, |
| 2524 |             "%serror%01uxx.", |
| 2525 |             error_handler, |
| 2526 |             status / 100); |
| 2527 |            break; |
| 2528 |          default: /* Handler for all errors */ |
| 2529 |            mg_snprintf(conn, |
| 2530 |             &truncated, |
| 2531 |             buf, |
| 2532 |             sizeof(buf) - 32, |
| 2533 |             "%serror.", |
| 2534 |             error_handler); |
| 2535 |            break; |
| 2536 |          } |
| 2537 | |
| 2538 |          /* String truncation in buf may only occur if error_handler |
| 2539 |           * is too long. This string is from the config, not from a |
| 2540 |           * client. */ |
| 2541 |          (void)truncated; |
| 2542 | |
| 2543 |          len = (int)strlen(buf); |
| 2544 | |
| 2545 |          tstr = strchr(error_page_file_ext, '.'); |
| 2546 | |
| 2547 |          while (tstr) { |
| 2548 |            for (i = 1; i < 32 && tstr[i] != 0 && tstr[i] != ','; |
| 2549 |             i++) |
| 2550 |              buf[len + i - 1] = tstr[i]; |
| 2551 |            buf[len + i - 1] = 0; |
| 2552 |            if (mg_stat(conn, buf, &error_page_file)) { |
| 2553 |              page_handler_found = 1; |
| 2554 |              break; |
| 2555 |            } |
| 2556 |            tstr = strchr(tstr + i, '.'); |
| 2557 |          } |
| 2558 |        } |
| 2559 |      } |
| 2560 | |
| 2561 |      if (page_handler_found) { |
| 2562 |        conn->in_error_handler = 1; |
| 2563 |        handle_file_based_request(conn, buf, &error_page_file); |
| 2564 |        conn->in_error_handler = 0; |
| 2565 |        return; |
| 2566 |      } |
| 2567 |    } |
| 2568 | |
| 2569 |    /* No custom error page. Send default error page. */ |
| 2570 |    gmt_time_string(date, sizeof(date), &curtime); |
| 2571 | |
| 2572 |    conn->must_close = 1; |
| 2573 |    mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text); |
| 2574 |    send_no_cache_header(conn); |
| 2575 |    mg_printf(conn, |
| 2576 |     "Date: %s\r\n" |
| 2577 |     "Connection: close\r\n\r\n", |
| 2578 |     date); |
| 2579 | |
| 2580 |    /* Errors 1xx, 204 and 304 MUST NOT send a body */ |
| 2581 |    if (status > 199 && status != 204 && status != 304) { |
| 2582 | |
| 2583 |      mg_printf(conn, "Error %d: %s\n", status, status_text); |
| 2584 | |
| 2585 |      if (fmt != NULL) { |
| 2586 |        va_start(ap, fmt); |
| 2587 |        mg_vsnprintf(conn, NULL, buf, sizeof(buf), fmt, ap); |
| 2588 |        va_end(ap); |
| 2589 |        mg_write(conn, buf, strlen(buf)); |
| 2590 |        DEBUG_TRACE("Error %i - [%s]", status, buf); |
| 2591 |      } |
| 2592 | |
| 2593 |    } else { |
| 2594 |      /* No body allowed. Close the connection. */ |
| 2595 |      DEBUG_TRACE("Error %i", status); |
| 2596 |    } |
| 2597 |  } |
| 2598 | } |
| 2599 | |
| 2600 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 2601 | /* Create substitutes for POSIX functions in Win32. */ |
| 2602 | |
| 2603 | #if defined(__MINGW32__) |
| 2604 | /* Show no warning in case system functions are not used. */ |
| 2605 | #pragma GCC diagnostic push |
| 2606 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 2607 | #endif |
| 2608 | |
| 2609 | |
| 2610 | static int |
| 2611 | pthread_mutex_init(pthread_mutex_t *mutex, void *unused) |
| 2612 | { |
| 2613 |  (void)unused; |
| 2614 |  *mutex = CreateMutex(NULL, FALSE, NULL); |
| 2615 |  return *mutex == NULL ? -1 : 0; |
| 2616 | } |
| 2617 | |
| 2618 | |
| 2619 | static int |
| 2620 | pthread_mutex_destroy(pthread_mutex_t *mutex) |
| 2621 | { |
| 2622 |  return CloseHandle(*mutex) == 0 ? -1 : 0; |
| 2623 | } |
| 2624 | |
| 2625 | |
| 2626 | static int |
| 2627 | pthread_mutex_lock(pthread_mutex_t *mutex) |
| 2628 | { |
| 2629 |  return WaitForSingleObject(*mutex, INFINITE) == WAIT_OBJECT_0 ? 0 : -1; |
| 2630 | } |
| 2631 | |
| 2632 | |
| 2633 | #ifdef ENABLE_UNUSED_PTHREAD_FUNCTIONS |
| 2634 | static int |
| 2635 | pthread_mutex_trylock(pthread_mutex_t *mutex) |
| 2636 | { |
| 2637 |  switch (WaitForSingleObject(*mutex, 0)) { |
| 2638 |  case WAIT_OBJECT_0: |
| 2639 |    return 0; |
| 2640 |  case WAIT_TIMEOUT: |
| 2641 |    return -2; /* EBUSY */ |
| 2642 |  } |
| 2643 |  return -1; |
| 2644 | } |
| 2645 | #endif |
| 2646 | |
| 2647 | |
| 2648 | static int |
| 2649 | pthread_mutex_unlock(pthread_mutex_t *mutex) |
| 2650 | { |
| 2651 |  return ReleaseMutex(*mutex) == 0 ? -1 : 0; |
| 2652 | } |
| 2653 | |
| 2654 | |
| 2655 | #ifndef WIN_PTHREADS_TIME_H |
| 2656 | static int |
| 2657 | clock_gettime(clockid_t clk_id, struct timespec *tp) |
| 2658 | { |
| 2659 |  FILETIME ft; |
| 2660 |  ULARGE_INTEGER li; |
| 2661 |  BOOL ok = FALSE; |
| 2662 |  double d; |
| 2663 |  static double perfcnt_per_sec = 0.0; |
| 2664 | |
| 2665 |  if (tp) { |
| 2666 |    memset(tp, 0, sizeof(*tp)); |
| 2667 |    if (clk_id == CLOCK_REALTIME) { |
| 2668 |      GetSystemTimeAsFileTime(&ft); |
| 2669 |      li.LowPart = ft.dwLowDateTime; |
| 2670 |      li.HighPart = ft.dwHighDateTime; |
| 2671 |      li.QuadPart -= 116444736000000000; /* 1.1.1970 in filedate */ |
| 2672 |      tp->tv_sec = (time_t)(li.QuadPart / 10000000); |
| 2673 |      tp->tv_nsec = (long)(li.QuadPart % 10000000) * 100; |
| 2674 |      ok = TRUE; |
| 2675 |    } else if (clk_id == CLOCK_MONOTONIC) { |
| 2676 |      if (perfcnt_per_sec == 0.0) { |
| 2677 |        QueryPerformanceFrequency((LARGE_INTEGER *)&li); |
| 2678 |        perfcnt_per_sec = 1.0 / li.QuadPart; |
| 2679 |      } |
| 2680 |      if (perfcnt_per_sec != 0.0) { |
| 2681 |        QueryPerformanceCounter((LARGE_INTEGER *)&li); |
| 2682 |        d = li.QuadPart * perfcnt_per_sec; |
| 2683 |        tp->tv_sec = (time_t)d; |
| 2684 |        d -= tp->tv_sec; |
| 2685 |        tp->tv_nsec = (long)(d * 1.0E9); |
| 2686 |        ok = TRUE; |
| 2687 |      } |
| 2688 |    } |
| 2689 |  } |
| 2690 | |
| 2691 |  return ok ? 0 : -1; |
| 2692 | } |
| 2693 | #endif |
| 2694 | |
| 2695 | |
| 2696 | static int |
| 2697 | pthread_cond_init(pthread_cond_t *cv, const void *unused) |
| 2698 | { |
| 2699 |  (void)unused; |
| 2700 |  InitializeCriticalSection(&cv->threadIdSec); |
| 2701 |  cv->waitingthreadcount = 0; |
| 2702 |  cv->waitingthreadhdls = |
| 2703 |   (pthread_t *)mg_calloc(MAX_WORKER_THREADS, sizeof(pthread_t)); |
| 2704 |  return (cv->waitingthreadhdls != NULL) ? 0 : -1; |
| 2705 | } |
| 2706 | |
| 2707 | |
| 2708 | static int |
| 2709 | pthread_cond_timedwait(pthread_cond_t *cv, |
| 2710 | pthread_mutex_t *mutex, |
| 2711 | const struct timespec *abstime) |
| 2712 | { |
| 2713 |  struct mg_workerTLS *tls = |
| 2714 |   (struct mg_workerTLS *)pthread_getspecific(sTlsKey); |
| 2715 |  int ok; |
| 2716 |  struct timespec tsnow; |
| 2717 |  int64_t nsnow, nswaitabs, nswaitrel; |
| 2718 |  DWORD mswaitrel; |
| 2719 | |
| 2720 |  EnterCriticalSection(&cv->threadIdSec); |
| 2721 |  assert(cv->waitingthreadcount < MAX_WORKER_THREADS); |
| 2722 |  cv->waitingthreadhdls[cv->waitingthreadcount] = |
| 2723 |   tls->pthread_cond_helper_mutex; |
| 2724 |  cv->waitingthreadcount++; |
| 2725 |  LeaveCriticalSection(&cv->threadIdSec); |
| 2726 | |
| 2727 |  if (abstime) { |
| 2728 |    clock_gettime(CLOCK_REALTIME, &tsnow); |
| 2729 |    nsnow = (((int64_t)tsnow.tv_sec) * 1000000000) + tsnow.tv_nsec; |
| 2730 |    nswaitabs = |
| 2731 |     (((int64_t)abstime->tv_sec) * 1000000000) + abstime->tv_nsec; |
| 2732 |    nswaitrel = nswaitabs - nsnow; |
| 2733 |    if (nswaitrel < 0) { |
| 2734 |      nswaitrel = 0; |
| 2735 |    } |
| 2736 |    mswaitrel = (DWORD)(nswaitrel / 1000000); |
| 2737 |  } else { |
| 2738 |    mswaitrel = INFINITE; |
| 2739 |  } |
| 2740 | |
| 2741 |  pthread_mutex_unlock(mutex); |
| 2742 |  ok = (WAIT_OBJECT_0 |
| 2743 |   == WaitForSingleObject(tls->pthread_cond_helper_mutex, mswaitrel)); |
| 2744 |  pthread_mutex_lock(mutex); |
| 2745 | |
| 2746 |  return ok ? 0 : -1; |
| 2747 | } |
| 2748 | |
| 2749 | |
| 2750 | static int |
| 2751 | pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex) |
| 2752 | { |
| 2753 |  return pthread_cond_timedwait(cv, mutex, NULL); |
| 2754 | } |
| 2755 | |
| 2756 | |
| 2757 | static int |
| 2758 | pthread_cond_signal(pthread_cond_t *cv) |
| 2759 | { |
| 2760 |  int i; |
| 2761 |  HANDLE wkup = NULL; |
| 2762 |  BOOL ok = FALSE; |
| 2763 | |
| 2764 |  EnterCriticalSection(&cv->threadIdSec); |
| 2765 |  if (cv->waitingthreadcount) { |
| 2766 |    wkup = cv->waitingthreadhdls[0]; |
| 2767 |    ok = SetEvent(wkup); |
| 2768 | |
| 2769 |    for (i = 1; i < cv->waitingthreadcount; i++) { |
| 2770 |      cv->waitingthreadhdls[i - 1] = cv->waitingthreadhdls[i]; |
| 2771 |    } |
| 2772 |    cv->waitingthreadcount--; |
| 2773 | |
| 2774 |    assert(ok); |
| 2775 |  } |
| 2776 |  LeaveCriticalSection(&cv->threadIdSec); |
| 2777 | |
| 2778 |  return ok ? 0 : 1; |
| 2779 | } |
| 2780 | |
| 2781 | |
| 2782 | static int |
| 2783 | pthread_cond_broadcast(pthread_cond_t *cv) |
| 2784 | { |
| 2785 |  EnterCriticalSection(&cv->threadIdSec); |
| 2786 |  while (cv->waitingthreadcount) { |
| 2787 |    pthread_cond_signal(cv); |
| 2788 |  } |
| 2789 |  LeaveCriticalSection(&cv->threadIdSec); |
| 2790 | |
| 2791 |  return 0; |
| 2792 | } |
| 2793 | |
| 2794 | |
| 2795 | static int |
| 2796 | pthread_cond_destroy(pthread_cond_t *cv) |
| 2797 | { |
| 2798 |  EnterCriticalSection(&cv->threadIdSec); |
| 2799 |  assert(cv->waitingthreadcount == 0); |
| 2800 |  mg_free(cv->waitingthreadhdls); |
| 2801 |  cv->waitingthreadhdls = 0; |
| 2802 |  LeaveCriticalSection(&cv->threadIdSec); |
| 2803 |  DeleteCriticalSection(&cv->threadIdSec); |
| 2804 | |
| 2805 |  return 0; |
| 2806 | } |
| 2807 | |
| 2808 | |
| 2809 | #if defined(__MINGW32__) |
| 2810 | /* Enable unused function warning again */ |
| 2811 | #pragma GCC diagnostic pop |
| 2812 | #endif |
| 2813 | |
| 2814 | |
| 2815 | /* For Windows, change all slashes to backslashes in path names. */ |
| 2816 | static void |
| 2817 | change_slashes_to_backslashes(char *path) |
| 2818 | { |
| 2819 |  int i; |
| 2820 | |
| 2821 |  for (i = 0; path[i] != '\0'; i++) { |
| 2822 |    if (path[i] == '/') { |
| 2823 |      path[i] = '\\'; |
| 2824 |    } |
| 2825 | |
| 2826 |    /* remove double backslash (check i > 0 to preserve UNC paths, |
| 2827 |     * like \\server\file.txt) */ |
| 2828 |    if ((path[i] == '\\') && (i > 0)) { |
| 2829 |      while (path[i + 1] == '\\' || path[i + 1] == '/') { |
| 2830 |        (void)memmove(path + i + 1, path + i + 2, strlen(path + i + 1)); |
| 2831 |      } |
| 2832 |    } |
| 2833 |  } |
| 2834 | } |
| 2835 | |
| 2836 | |
| 2837 | static int |
| 2838 | mg_wcscasecmp(const wchar_t *s1, const wchar_t *s2) |
| 2839 | { |
| 2840 |  int diff; |
| 2841 | |
| 2842 |  do { |
| 2843 |    diff = tolower(*s1) - tolower(*s2); |
| 2844 |    s1++; |
| 2845 |    s2++; |
| 2846 |  } while (diff == 0 && s1[-1] != '\0'); |
| 2847 | |
| 2848 |  return diff; |
| 2849 | } |
| 2850 | |
| 2851 | |
| 2852 | /* Encode 'path' which is assumed UTF-8 string, into UNICODE string. |
| 2853 | * wbuf and wbuf_len is a target buffer and its length. */ |
| 2854 | static void |
| 2855 | path_to_unicode(const struct mg_connection *conn, |
| 2856 | const char *path, |
| 2857 | wchar_t *wbuf, |
| 2858 | size_t wbuf_len) |
| 2859 | { |
| 2860 |  char buf[PATH_MAX], buf2[PATH_MAX]; |
| 2861 |  wchar_t wbuf2[MAX_PATH + 1]; |
| 2862 |  DWORD long_len, err; |
| 2863 |  int (*fcompare)(const wchar_t *, const wchar_t *) = mg_wcscasecmp; |
| 2864 | |
| 2865 |  mg_strlcpy(buf, path, sizeof(buf)); |
| 2866 |  change_slashes_to_backslashes(buf); |
| 2867 | |
| 2868 |  /* Convert to Unicode and back. If doubly-converted string does not |
| 2869 |   * match the original, something is fishy, reject. */ |
| 2870 |  memset(wbuf, 0, wbuf_len * sizeof(wchar_t)); |
| 2871 |  MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int)wbuf_len); |
| 2872 |  WideCharToMultiByte( |
| 2873 |   CP_UTF8, 0, wbuf, (int)wbuf_len, buf2, sizeof(buf2), NULL, NULL); |
| 2874 |  if (strcmp(buf, buf2) != 0) { |
| 2875 |    wbuf[0] = L'\0'; |
| 2876 |  } |
| 2877 | |
| 2878 |  /* TODO: Add a configuration to switch between case sensitive and |
| 2879 |   * case insensitive URIs for Windows server. */ |
| 2880 |  /* |
| 2881 |  if (conn) { |
| 2882 |   if (conn->ctx->config[WINDOWS_CASE_SENSITIVE]) { |
| 2883 |   fcompare = wcscmp; |
| 2884 |   } |
| 2885 |  } |
| 2886 |  */ |
| 2887 |  (void)conn; /* conn is currently unused */ |
| 2888 | |
| 2889 |  /* Only accept a full file path, not a Windows short (8.3) path. */ |
| 2890 |  memset(wbuf2, 0, ARRAY_SIZE(wbuf2) * sizeof(wchar_t)); |
| 2891 |  long_len = GetLongPathNameW(wbuf, wbuf2, ARRAY_SIZE(wbuf2) - 1); |
| 2892 |  if (long_len == 0) { |
| 2893 |    err = GetLastError(); |
| 2894 |    if (err == ERROR_FILE_NOT_FOUND) { |
| 2895 |      /* File does not exist. This is not always a problem here. */ |
| 2896 |      return; |
| 2897 |    } |
| 2898 |  } |
| 2899 |  if ( ) { |
| 2900 |    /* Short name is used. */ |
| 2901 |    wbuf[0] = L'\0'; |
| 2902 |  } |
| 2903 | } |
| 2904 | |
| 2905 | |
| 2906 | #if defined(_WIN32_WCE) |
| 2907 | /* Create substitutes for POSIX functions in Win32. */ |
| 2908 | |
| 2909 | #if defined(__MINGW32__) |
| 2910 | /* Show no warning in case system functions are not used. */ |
| 2911 | #pragma GCC diagnostic push |
| 2912 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 2913 | #endif |
| 2914 | |
| 2915 | |
| 2916 | static time_t |
| 2917 | time(time_t *ptime) |
| 2918 | { |
| 2919 |  time_t t; |
| 2920 |  SYSTEMTIME st; |
| 2921 |  FILETIME ft; |
| 2922 | |
| 2923 |  GetSystemTime(&st); |
| 2924 |  SystemTimeToFileTime(&st, &ft); |
| 2925 |  t = SYS2UNIX_TIME(ft.dwLowDateTime, ft.dwHighDateTime); |
| 2926 | |
| 2927 |  if (ptime != NULL) { |
| 2928 |    *ptime = t; |
| 2929 |  } |
| 2930 | |
| 2931 |  return t; |
| 2932 | } |
| 2933 | |
| 2934 | |
| 2935 | static struct tm * |
| 2936 | localtime(const time_t *ptime, struct tm *ptm) |
| 2937 | { |
| 2938 |  int64_t t = ((int64_t)*ptime) * RATE_DIFF + EPOCH_DIFF; |
| 2939 |  FILETIME ft, lft; |
| 2940 |  SYSTEMTIME st; |
| 2941 |  TIME_ZONE_INFORMATION tzinfo; |
| 2942 | |
| 2943 |  if (ptm == NULL) { |
| 2944 |    return NULL; |
| 2945 |  } |
| 2946 | |
| 2947 |  *(int64_t *)&ft = t; |
| 2948 |  FileTimeToLocalFileTime(&ft, &lft); |
| 2949 |  FileTimeToSystemTime(&lft, &st); |
| 2950 |  ptm->tm_year = st.wYear - 1900; |
| 2951 |  ptm->tm_mon = st.wMonth - 1; |
| 2952 |  ptm->tm_wday = st.wDayOfWeek; |
| 2953 |  ptm->tm_mday = st.wDay; |
| 2954 |  ptm->tm_hour = st.wHour; |
| 2955 |  ptm->tm_min = st.wMinute; |
| 2956 |  ptm->tm_sec = st.wSecond; |
| 2957 |  ptm->tm_yday = 0; /* hope nobody uses this */ |
| 2958 |  ptm->tm_isdst = |
| 2959 |   GetTimeZoneInformation(&tzinfo) == TIME_ZONE_ID_DAYLIGHT ? 1 : 0; |
| 2960 | |
| 2961 |  return ptm; |
| 2962 | } |
| 2963 | |
| 2964 | |
| 2965 | static struct tm * |
| 2966 | gmtime(const time_t *ptime, struct tm *ptm) |
| 2967 | { |
| 2968 |  /* FIXME(lsm): fix this. */ |
| 2969 |  return localtime(ptime, ptm); |
| 2970 | } |
| 2971 | |
| 2972 | |
| 2973 | static size_t |
| 2974 | strftime(char *dst, size_t dst_size, const char *fmt, const struct tm *tm) |
| 2975 | { |
| 2976 |  (void)mg_snprintf(NULL, dst, dst_size, "implement strftime() for WinCE"); |
| 2977 |  return 0; |
| 2978 | } |
| 2979 | |
| 2980 | |
| 2981 | #if defined(__MINGW32__) |
| 2982 | /* Enable unused function warning again */ |
| 2983 | #pragma GCC diagnostic pop |
| 2984 | #endif |
| 2985 | |
| 2986 | #endif |
| 2987 | |
| 2988 | |
| 2989 | /* Windows happily opens files with some garbage at the end of file name. |
| 2990 | * For example, fopen("a.cgi ", "r") on Windows successfully opens |
| 2991 | * "a.cgi", despite one would expect an error back. |
| 2992 | * This function returns non-0 if path ends with some garbage. */ |
| 2993 | static int |
| 2994 | path_cannot_disclose_cgi(const char *path) |
| 2995 | { |
| 2996 |  static const char *allowed_last_characters = "_-"; |
| 2997 |  int last = path[strlen(path) - 1]; |
| 2998 |  return isalnum(last) || strchr(allowed_last_characters, last) != NULL; |
| 2999 | } |
| 3000 | |
| 3001 | |
| 3002 | static int |
| 3003 | mg_stat(struct mg_connection *conn, const char *path, struct file *filep) |
| 3004 | { |
| 3005 |  wchar_t wbuf[PATH_MAX]; |
| 3006 |  WIN32_FILE_ATTRIBUTE_DATA info; |
| 3007 |  time_t creation_time; |
| 3008 | |
| 3009 |  if (!filep) { |
| 3010 |    return 0; |
| 3011 |  } |
| 3012 |  memset(filep, 0, sizeof(*filep)); |
| 3013 | |
| 3014 |  if (conn && is_file_in_memory(conn, path, filep)) { |
| 3015 |    /* filep->is_directory = 0; filep->gzipped = 0; .. already done by |
| 3016 |     * memset */ |
| 3017 |    filep->last_modified = time(NULL); |
| 3018 |    /* last_modified = now ... assumes the file may change during runtime, |
| 3019 |     * so every mg_fopen call may return different data */ |
| 3020 |    /* last_modified = conn->ctx.start_time; |
| 3021 |     * May be used it the data does not change during runtime. This allows |
| 3022 |     * browser caching. Since we do not know, we have to assume the file |
| 3023 |     * in memory may change. */ |
| 3024 |    return 1; |
| 3025 |  } |
| 3026 | |
| 3027 | if (path && path[4] == 0 && memcmp(path, "www/", 4) == 0) |
| 3028 | { |
| 3029 | filep->size = 512; |
| 3030 | filep->is_directory = 1; |
| 3031 | return 1; |
| 3032 | } |
| 3033 | |
| 3034 |  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); |
| 3035 |  if (GetFileAttributesExW(wbuf, GetFileExInfoStandard, &info) != 0) { |
| 3036 |    filep->size = MAKEUQUAD(info.nFileSizeLow, info.nFileSizeHigh); |
| 3037 |    filep->last_modified = |
| 3038 |     SYS2UNIX_TIME(info.ftLastWriteTime.dwLowDateTime, |
| 3039 |     info.ftLastWriteTime.dwHighDateTime); |
| 3040 | |
| 3041 |    /* On Windows, the file creation time can be higher than the |
| 3042 |     * modification time, e.g. when a file is copied. |
| 3043 |     * Since the Last-Modified timestamp is used for caching |
| 3044 |     * it should be based on the most recent timestamp. */ |
| 3045 |    creation_time = SYS2UNIX_TIME(info.ftCreationTime.dwLowDateTime, |
| 3046 |     info.ftCreationTime.dwHighDateTime); |
| 3047 |    if (creation_time > filep->last_modified) { |
| 3048 |      filep->last_modified = creation_time; |
| 3049 |    } |
| 3050 | |
| 3051 |    filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; |
| 3052 |    /* If file name is fishy, reset the file structure and return |
| 3053 |     * error. |
| 3054 |     * Note it is important to reset, not just return the error, cause |
| 3055 |     * functions like is_file_opened() check the struct. */ |
| 3056 |    if (!filep->is_directory && !path_cannot_disclose_cgi(path)) { |
| 3057 |      memset(filep, 0, sizeof(*filep)); |
| 3058 |      return 0; |
| 3059 |    } |
| 3060 | |
| 3061 |    return 1; |
| 3062 |  } |
| 3063 | |
| 3064 |  return 0; |
| 3065 | } |
| 3066 | |
| 3067 | |
| 3068 | static int |
| 3069 | mg_remove(const struct mg_connection *conn, const char *path) |
| 3070 | { |
| 3071 |  wchar_t wbuf[PATH_MAX]; |
| 3072 |  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); |
| 3073 |  return DeleteFileW(wbuf) ? 0 : -1; |
| 3074 | } |
| 3075 | |
| 3076 | |
| 3077 | static int |
| 3078 | mg_mkdir(const struct mg_connection *conn, const char *path, int mode) |
| 3079 | { |
| 3080 |  wchar_t wbuf[PATH_MAX]; |
| 3081 |  (void)mode; |
| 3082 |  path_to_unicode(conn, path, wbuf, ARRAY_SIZE(wbuf)); |
| 3083 |  return CreateDirectoryW(wbuf, NULL) ? 0 : -1; |
| 3084 | } |
| 3085 | |
| 3086 | |
| 3087 | /* Create substitutes for POSIX functions in Win32. */ |
| 3088 | |
| 3089 | #if defined(__MINGW32__) |
| 3090 | /* Show no warning in case system functions are not used. */ |
| 3091 | #pragma GCC diagnostic push |
| 3092 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 3093 | #endif |
| 3094 | |
| 3095 | |
| 3096 | /* Implementation of POSIX opendir/closedir/readdir for Windows. */ |
| 3097 | static DIR * |
| 3098 | mg_opendir(const struct mg_connection *conn, const char *name) |
| 3099 | { |
| 3100 |  DIR *dir = NULL; |
| 3101 |  wchar_t wpath[PATH_MAX]; |
| 3102 |  DWORD attrs; |
| 3103 | |
| 3104 |  if (name == NULL) { |
| 3105 |    SetLastError(ERROR_BAD_ARGUMENTS); |
| 3106 |  } else if ((dir = (DIR *)mg_malloc(sizeof(*dir))) == NULL) { |
| 3107 |    SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
| 3108 |  } else { |
| 3109 |    path_to_unicode(conn, name, wpath, ARRAY_SIZE(wpath)); |
| 3110 |    attrs = GetFileAttributesW(wpath); |
| 3111 |    if (attrs != 0xFFFFFFFF && ((attrs & FILE_ATTRIBUTE_DIRECTORY) |
| 3112 |     == FILE_ATTRIBUTE_DIRECTORY)) { |
| 3113 |      (void)wcscat(wpath, L"\\*"); |
| 3114 |      dir->handle = FindFirstFileW(wpath, &dir->info); |
| 3115 |      dir->result.d_name[0] = '\0'; |
| 3116 |    } else { |
| 3117 |      mg_free(dir); |
| 3118 |      dir = NULL; |
| 3119 |    } |
| 3120 |  } |
| 3121 | |
| 3122 |  return dir; |
| 3123 | } |
| 3124 | |
| 3125 | |
| 3126 | static int |
| 3127 | mg_closedir(DIR *dir) |
| 3128 | { |
| 3129 |  int result = 0; |
| 3130 | |
| 3131 |  if (dir != NULL) { |
| 3132 |    if (dir->handle != INVALID_HANDLE_VALUE) |
| 3133 |      result = FindClose(dir->handle) ? 0 : -1; |
| 3134 | |
| 3135 |    mg_free(dir); |
| 3136 |  } else { |
| 3137 |    result = -1; |
| 3138 |    SetLastError(ERROR_BAD_ARGUMENTS); |
| 3139 |  } |
| 3140 | |
| 3141 |  return result; |
| 3142 | } |
| 3143 | |
| 3144 | |
| 3145 | static struct dirent * |
| 3146 | mg_readdir(DIR *dir) |
| 3147 | { |
| 3148 |  struct dirent *result = 0; |
| 3149 | |
| 3150 |  if (dir) { |
| 3151 |    if (dir->handle != INVALID_HANDLE_VALUE) { |
| 3152 |      result = &dir->result; |
| 3153 |      (void)WideCharToMultiByte(CP_UTF8, |
| 3154 |       0, |
| 3155 |       dir->info.cFileName, |
| 3156 |       -1, |
| 3157 |       result->d_name, |
| 3158 |       sizeof(result->d_name), |
| 3159 |       NULL, |
| 3160 |       NULL); |
| 3161 | |
| 3162 |      if (!FindNextFileW(dir->handle, &dir->info)) { |
| 3163 |        (void)FindClose(dir->handle); |
| 3164 |        dir->handle = INVALID_HANDLE_VALUE; |
| 3165 |      } |
| 3166 | |
| 3167 |    } else { |
| 3168 |      SetLastError(ERROR_FILE_NOT_FOUND); |
| 3169 |    } |
| 3170 |  } else { |
| 3171 |    SetLastError(ERROR_BAD_ARGUMENTS); |
| 3172 |  } |
| 3173 | |
| 3174 |  return result; |
| 3175 | } |
| 3176 | |
| 3177 | |
| 3178 | #ifndef HAVE_POLL |
| 3179 | static int |
| 3180 | poll(struct pollfd *pfd, unsigned int n, int milliseconds) |
| 3181 | { |
| 3182 |  struct timeval tv; |
| 3183 |  fd_set set; |
| 3184 |  unsigned int i; |
| 3185 |  int result; |
| 3186 |  SOCKET maxfd = 0; |
| 3187 | |
| 3188 |  memset(&tv, 0, sizeof(tv)); |
| 3189 |  tv.tv_sec = milliseconds / 1000; |
| 3190 |  tv.tv_usec = (milliseconds % 1000) * 1000; |
| 3191 |  FD_ZERO(&set); |
| 3192 | |
| 3193 |  for (i = 0; i < n; i++) { |
| 3194 |    FD_SET((SOCKET)pfd[i].fd, &set); |
| 3195 |    pfd[i].revents = 0; |
| 3196 | |
| 3197 |    if (pfd[i].fd > maxfd) { |
| 3198 |      maxfd = pfd[i].fd; |
| 3199 |    } |
| 3200 |  } |
| 3201 | |
| 3202 |  if ((result = select((int)maxfd + 1, &set, NULL, NULL, &tv)) > 0) { |
| 3203 |    for (i = 0; i < n; i++) { |
| 3204 |      if (FD_ISSET(pfd[i].fd, &set)) { |
| 3205 |        pfd[i].revents = POLLIN; |
| 3206 |      } |
| 3207 |    } |
| 3208 |  } |
| 3209 | |
| 3210 |  return result; |
| 3211 | } |
| 3212 | #endif /* HAVE_POLL */ |
| 3213 | |
| 3214 | #if defined(__MINGW32__) |
| 3215 | /* Enable unused function warning again */ |
| 3216 | #pragma GCC diagnostic pop |
| 3217 | #endif |
| 3218 | |
| 3219 | |
| 3220 | static void |
| 3221 | set_close_on_exec(SOCKET sock, struct mg_connection *conn /* may be null */) |
| 3222 | { |
| 3223 |  (void)conn; /* Unused. */ |
| 3224 |  (void)SetHandleInformation((HANDLE)(intptr_t)sock, HANDLE_FLAG_INHERIT, 0); |
| 3225 | } |
| 3226 | |
| 3227 | |
| 3228 | int |
| 3229 | mg_start_thread(mg_thread_func_t f, void *p) |
| 3230 | { |
| 3231 | #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) |
| 3232 |  /* Compile-time option to control stack size, e.g. -DUSE_STACK_SIZE=16384 |
| 3233 |   */ |
| 3234 |  return ((_beginthread((void(__cdecl *)(void *))f, USE_STACK_SIZE, p) |
| 3235 |   == ((uintptr_t)(-1L))) |
| 3236 |   ? -1 |
| 3237 |   : 0); |
| 3238 | #else |
| 3239 |  return ( |
| 3240 |   (_beginthread((void(__cdecl *)(void *))f, 0, p) == ((uintptr_t)(-1L))) |
| 3241 |   ? -1 |
| 3242 |   : 0); |
| 3243 | #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ |
| 3244 | } |
| 3245 | |
| 3246 | |
| 3247 | /* Start a thread storing the thread context. */ |
| 3248 | static int |
| 3249 | mg_start_thread_with_id(unsigned(__stdcall *f)(void *), |
| 3250 | void *p, |
| 3251 | pthread_t *threadidptr) |
| 3252 | { |
| 3253 |  uintptr_t uip; |
| 3254 |  HANDLE threadhandle; |
| 3255 |  int result = -1; |
| 3256 | |
| 3257 |  uip = _beginthreadex(NULL, 0, (unsigned(__stdcall *)(void *))f, p, 0, NULL); |
| 3258 |  threadhandle = (HANDLE)uip; |
| 3259 |  if ((uip != (uintptr_t)(-1L)) && (threadidptr != NULL)) { |
| 3260 |    *threadidptr = threadhandle; |
| 3261 |    result = 0; |
| 3262 |  } |
| 3263 | |
| 3264 |  return result; |
| 3265 | } |
| 3266 | |
| 3267 | |
| 3268 | /* Wait for a thread to finish. */ |
| 3269 | static int |
| 3270 | mg_join_thread(pthread_t threadid) |
| 3271 | { |
| 3272 |  int result; |
| 3273 |  DWORD dwevent; |
| 3274 | |
| 3275 |  result = -1; |
| 3276 |  dwevent = WaitForSingleObject(threadid, INFINITE); |
| 3277 |  if (dwevent == WAIT_FAILED) { |
| 3278 |    DEBUG_TRACE("WaitForSingleObject() failed, error %d", ERRNO); |
| 3279 |  } else { |
| 3280 |    if (dwevent == WAIT_OBJECT_0) { |
| 3281 |      CloseHandle(threadid); |
| 3282 |      result = 0; |
| 3283 |    } |
| 3284 |  } |
| 3285 | |
| 3286 |  return result; |
| 3287 | } |
| 3288 | |
| 3289 | #if !defined(NO_SSL_DL) |
| 3290 | /* Create substitutes for POSIX functions in Win32. */ |
| 3291 | |
| 3292 | #if defined(__MINGW32__) |
| 3293 | /* Show no warning in case system functions are not used. */ |
| 3294 | #pragma GCC diagnostic push |
| 3295 | #pragma GCC diagnostic ignored "-Wunused-function" |
| 3296 | #endif |
| 3297 | |
| 3298 | |
| 3299 | static HANDLE |
| 3300 | dlopen(const char *dll_name, int flags) |
| 3301 | { |
| 3302 |  wchar_t wbuf[PATH_MAX]; |
| 3303 |  (void)flags; |
| 3304 |  path_to_unicode(NULL, dll_name, wbuf, ARRAY_SIZE(wbuf)); |
| 3305 |  return LoadLibraryW(wbuf); |
| 3306 | } |
| 3307 | |
| 3308 | |
| 3309 | static int |
| 3310 | dlclose(void *handle) |
| 3311 | { |
| 3312 |  int result; |
| 3313 | |
| 3314 |  if (FreeLibrary((HMODULE)handle) != 0) { |
| 3315 |    result = 0; |
| 3316 |  } else { |
| 3317 |    result = -1; |
| 3318 |  } |
| 3319 | |
| 3320 |  return result; |
| 3321 | } |
| 3322 | |
| 3323 | |
| 3324 | #if defined(__MINGW32__) |
| 3325 | /* Enable unused function warning again */ |
| 3326 | #pragma GCC diagnostic pop |
| 3327 | #endif |
| 3328 | |
| 3329 | #endif |
| 3330 | |
| 3331 | |
| 3332 | #if !defined(NO_CGI) |
| 3333 | #define SIGKILL (0) |
| 3334 | |
| 3335 | static int |
| 3336 | kill(pid_t pid, int sig_num) |
| 3337 | { |
| 3338 |  (void)TerminateProcess((HANDLE)pid, (UINT)sig_num); |
| 3339 |  (void)CloseHandle((HANDLE)pid); |
| 3340 |  return 0; |
| 3341 | } |
| 3342 | |
| 3343 | |
| 3344 | static void |
| 3345 | trim_trailing_whitespaces(char *s) |
| 3346 | { |
| 3347 |  char *e = s + strlen(s) - 1; |
| 3348 |  while (e > s && isspace(*(unsigned char *)e)) { |
| 3349 |    *e-- = '\0'; |
| 3350 |  } |
| 3351 | } |
| 3352 | |
| 3353 | |
| 3354 | static pid_t |
| 3355 | spawn_process(struct mg_connection *conn, |
| 3356 | const char *prog, |
| 3357 | char *envblk, |
| 3358 | char *envp[], |
| 3359 | int fdin[2], |
| 3360 | int fdout[2], |
| 3361 | int fderr[2], |
| 3362 | const char *dir) |
| 3363 | { |
| 3364 |  HANDLE me; |
| 3365 |  char *p, *interp, full_interp[PATH_MAX], full_dir[PATH_MAX], |
| 3366 |   cmdline[PATH_MAX], buf[PATH_MAX]; |
| 3367 |  int truncated; |
| 3368 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 3369 |  STARTUPINFOA si; |
| 3370 |  PROCESS_INFORMATION pi = {0}; |
| 3371 | |
| 3372 |  (void)envp; |
| 3373 | |
| 3374 |  memset(&si, 0, sizeof(si)); |
| 3375 |  si.cb = sizeof(si); |
| 3376 | |
| 3377 |  si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; |
| 3378 |  si.wShowWindow = SW_HIDE; |
| 3379 | |
| 3380 |  me = GetCurrentProcess(); |
| 3381 |  DuplicateHandle(me, |
| 3382 |   (HANDLE)_get_osfhandle(fdin[0]), |
| 3383 |   me, |
| 3384 |   &si.hStdInput, |
| 3385 |   0, |
| 3386 |   TRUE, |
| 3387 |   DUPLICATE_SAME_ACCESS); |
| 3388 |  DuplicateHandle(me, |
| 3389 |   (HANDLE)_get_osfhandle(fdout[1]), |
| 3390 |   me, |
| 3391 |   &si.hStdOutput, |
| 3392 |   0, |
| 3393 |   TRUE, |
| 3394 |   DUPLICATE_SAME_ACCESS); |
| 3395 |  DuplicateHandle(me, |
| 3396 |   (HANDLE)_get_osfhandle(fderr[1]), |
| 3397 |   me, |
| 3398 |   &si.hStdError, |
| 3399 |   0, |
| 3400 |   TRUE, |
| 3401 |   DUPLICATE_SAME_ACCESS); |
| 3402 | |
| 3403 |  /* Mark handles that should not be inherited. See |
| 3404 |   * https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499%28v=vs.85%29.aspx |
| 3405 |   */ |
| 3406 |  SetHandleInformation((HANDLE)_get_osfhandle(fdin[1]), |
| 3407 |   HANDLE_FLAG_INHERIT, |
| 3408 |   0); |
| 3409 |  SetHandleInformation((HANDLE)_get_osfhandle(fdout[0]), |
| 3410 |   HANDLE_FLAG_INHERIT, |
| 3411 |   0); |
| 3412 |  SetHandleInformation((HANDLE)_get_osfhandle(fderr[0]), |
| 3413 |   HANDLE_FLAG_INHERIT, |
| 3414 |   0); |
| 3415 | |
| 3416 |  /* If CGI file is a script, try to read the interpreter line */ |
| 3417 |  interp = conn->ctx->config[CGI_INTERPRETER]; |
| 3418 |  if (interp == NULL) { |
| 3419 |    buf[0] = buf[1] = '\0'; |
| 3420 | |
| 3421 |    /* Read the first line of the script into the buffer */ |
| 3422 |    mg_snprintf( |
| 3423 |     conn, &truncated, cmdline, sizeof(cmdline), "%s/%s", dir, prog); |
| 3424 | |
| 3425 |    if (truncated) { |
| 3426 |      pi.hProcess = (pid_t)-1; |
| 3427 |      goto spawn_cleanup; |
| 3428 |    } |
| 3429 | |
| 3430 |    if (mg_fopen(conn, cmdline, "r", &file)) { |
| 3431 |      p = (char *)file.membuf; |
| 3432 |      mg_fgets(buf, sizeof(buf), &file, &p); |
| 3433 |      mg_fclose(&file); |
| 3434 |      buf[sizeof(buf) - 1] = '\0'; |
| 3435 |    } |
| 3436 | |
| 3437 |    if (buf[0] == '#' && buf[1] == '!') { |
| 3438 |      trim_trailing_whitespaces(buf + 2); |
| 3439 |    } else { |
| 3440 |      buf[2] = '\0'; |
| 3441 |    } |
| 3442 |    interp = buf + 2; |
| 3443 |  } |
| 3444 | |
| 3445 |  if (interp[0] != '\0') { |
| 3446 |    GetFullPathNameA(interp, sizeof(full_interp), full_interp, NULL); |
| 3447 |    interp = full_interp; |
| 3448 |  } |
| 3449 |  GetFullPathNameA(dir, sizeof(full_dir), full_dir, NULL); |
| 3450 | |
| 3451 |  if (interp[0] != '\0') { |
| 3452 |    mg_snprintf(conn, |
| 3453 |     &truncated, |
| 3454 |     cmdline, |
| 3455 |     sizeof(cmdline), |
| 3456 |     "\"%s\" \"%s\\%s\"", |
| 3457 |     interp, |
| 3458 |     full_dir, |
| 3459 |     prog); |
| 3460 |  } else { |
| 3461 |    mg_snprintf(conn, |
| 3462 |     &truncated, |
| 3463 |     cmdline, |
| 3464 |     sizeof(cmdline), |
| 3465 |     "\"%s\\%s\"", |
| 3466 |     full_dir, |
| 3467 |     prog); |
| 3468 |  } |
| 3469 | |
| 3470 |  if (truncated) { |
| 3471 |    pi.hProcess = (pid_t)-1; |
| 3472 |    goto spawn_cleanup; |
| 3473 |  } |
| 3474 | |
| 3475 |  DEBUG_TRACE("Running [%s]", cmdline); |
| 3476 |  if (CreateProcessA(NULL, |
| 3477 |   cmdline, |
| 3478 |   NULL, |
| 3479 |   NULL, |
| 3480 |   TRUE, |
| 3481 |   CREATE_NEW_PROCESS_GROUP, |
| 3482 |   envblk, |
| 3483 |   NULL, |
| 3484 |   &si, |
| 3485 |   &pi) == 0) { |
| 3486 |    mg_cry( |
| 3487 |     conn, "%s: CreateProcess(%s): %ld", __func__, cmdline, (long)ERRNO); |
| 3488 |    pi.hProcess = (pid_t)-1; |
| 3489 |    /* goto spawn_cleanup; */ |
| 3490 |  } |
| 3491 | |
| 3492 | spawn_cleanup: |
| 3493 |  (void)CloseHandle(si.hStdOutput); |
| 3494 |  (void)CloseHandle(si.hStdError); |
| 3495 |  (void)CloseHandle(si.hStdInput); |
| 3496 |  if (pi.hThread != NULL) { |
| 3497 |    (void)CloseHandle(pi.hThread); |
| 3498 |  } |
| 3499 | |
| 3500 |  return (pid_t)pi.hProcess; |
| 3501 | } |
| 3502 | #endif /* !NO_CGI */ |
| 3503 | |
| 3504 | |
| 3505 | static int |
| 3506 | set_non_blocking_mode(SOCKET sock) |
| 3507 | { |
| 3508 |  unsigned long on = 1; |
| 3509 |  return ioctlsocket(sock, (long)FIONBIO, &on); |
| 3510 | } |
| 3511 | |
| 3512 | #else |
| 3513 | |
| 3514 | static int |
| 3515 | mg_stat(struct mg_connection *conn, const char *path, struct file *filep) |
| 3516 | { |
| 3517 |  struct stat st; |
| 3518 |  if (!filep) { |
| 3519 |    return 0; |
| 3520 |  } |
| 3521 |  memset(filep, 0, sizeof(*filep)); |
| 3522 | |
| 3523 |  if (conn && is_file_in_memory(conn, path, filep)) { |
| 3524 |    return 1; |
| 3525 |  } |
| 3526 | |
| 3527 | if (path && path[4] == 0 && memcmp(path, "www/", 4) == 0) |
| 3528 | { |
| 3529 | filep->size = 512; |
| 3530 | filep->is_directory = 1; |
| 3531 | return 1; |
| 3532 | } |
| 3533 | |
| 3534 |  if (0 == stat(path, &st)) { |
| 3535 |    filep->size = (uint64_t)(st.st_size); |
| 3536 |    filep->last_modified = st.st_mtime; |
| 3537 |    filep->is_directory = S_ISDIR(st.st_mode); |
| 3538 |    return 1; |
| 3539 |  } |
| 3540 | |
| 3541 |  return 0; |
| 3542 | } |
| 3543 | |
| 3544 | |
| 3545 | static void |
| 3546 | set_close_on_exec(SOCKET fd, struct mg_connection *conn /* may be null */) |
| 3547 | { |
| 3548 |  if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { |
| 3549 |    if (conn) { |
| 3550 |      mg_cry(conn, |
| 3551 |       "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", |
| 3552 |       __func__, |
| 3553 |       strerror(ERRNO)); |
| 3554 |    } |
| 3555 |  } |
| 3556 | } |
| 3557 | |
| 3558 | |
| 3559 | int |
| 3560 | mg_start_thread(mg_thread_func_t func, void *param) |
| 3561 | { |
| 3562 |  pthread_t thread_id; |
| 3563 |  pthread_attr_t attr; |
| 3564 |  int result; |
| 3565 | |
| 3566 |  (void)pthread_attr_init(&attr); |
| 3567 |  (void)pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); |
| 3568 | |
| 3569 | #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) |
| 3570 |  /* Compile-time option to control stack size, |
| 3571 |   * e.g. -DUSE_STACK_SIZE=16384 */ |
| 3572 |  (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); |
| 3573 | #endif /* defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) */ |
| 3574 | |
| 3575 |  result = pthread_create(&thread_id, &attr, func, param); |
| 3576 |  pthread_attr_destroy(&attr); |
| 3577 | |
| 3578 |  return result; |
| 3579 | } |
| 3580 | |
| 3581 | |
| 3582 | /* Start a thread storing the thread context. */ |
| 3583 | static int |
| 3584 | mg_start_thread_with_id(mg_thread_func_t func, |
| 3585 | void *param, |
| 3586 | pthread_t *threadidptr) |
| 3587 | { |
| 3588 |  pthread_t thread_id; |
| 3589 |  pthread_attr_t attr; |
| 3590 |  int result; |
| 3591 | |
| 3592 |  (void)pthread_attr_init(&attr); |
| 3593 | |
| 3594 | #if defined(USE_STACK_SIZE) && (USE_STACK_SIZE > 1) |
| 3595 |  /* Compile-time option to control stack size, |
| 3596 |   * e.g. -DUSE_STACK_SIZE=16384 */ |
| 3597 |  (void)pthread_attr_setstacksize(&attr, USE_STACK_SIZE); |
| 3598 | #endif /* defined(USE_STACK_SIZE) && USE_STACK_SIZE > 1 */ |
| 3599 | |
| 3600 |  result = pthread_create(&thread_id, &attr, func, param); |
| 3601 |  pthread_attr_destroy(&attr); |
| 3602 |  if ((result == 0) && (threadidptr != NULL)) { |
| 3603 |    *threadidptr = thread_id; |
| 3604 |  } |
| 3605 |  return result; |
| 3606 | } |
| 3607 | |
| 3608 | |
| 3609 | /* Wait for a thread to finish. */ |
| 3610 | static int |
| 3611 | mg_join_thread(pthread_t threadid) |
| 3612 | { |
| 3613 |  int result; |
| 3614 | |
| 3615 |  result = pthread_join(threadid, NULL); |
| 3616 |  return result; |
| 3617 | } |
| 3618 | |
| 3619 | |
| 3620 | #ifndef NO_CGI |
| 3621 | static pid_t |
| 3622 | spawn_process(struct mg_connection *conn, |
| 3623 | const char *prog, |
| 3624 | char *envblk, |
| 3625 | char *envp[], |
| 3626 | int fdin[2], |
| 3627 | int fdout[2], |
| 3628 | int fderr[2], |
| 3629 | const char *dir) |
| 3630 | { |
| 3631 |  pid_t pid; |
| 3632 |  const char *interp; |
| 3633 | |
| 3634 |  (void)envblk; |
| 3635 | |
| 3636 |  if (conn == NULL) { |
| 3637 |    return 0; |
| 3638 |  } |
| 3639 | |
| 3640 |  if ((pid = fork()) == -1) { |
| 3641 |    /* Parent */ |
| 3642 |    send_http_error(conn, |
| 3643 |     500, |
| 3644 |     "Error: Creating CGI process\nfork(): %s", |
| 3645 |     strerror(ERRNO)); |
| 3646 |  } else if (pid == 0) { |
| 3647 |    /* Child */ |
| 3648 |    if (chdir(dir) != 0) { |
| 3649 |      mg_cry(conn, "%s: chdir(%s): %s", __func__, dir, strerror(ERRNO)); |
| 3650 |    } else if (dup2(fdin[0], 0) == -1) { |
| 3651 |      mg_cry(conn, |
| 3652 |       "%s: dup2(%d, 0): %s", |
| 3653 |       __func__, |
| 3654 |       fdin[0], |
| 3655 |       strerror(ERRNO)); |
| 3656 |    } else if (dup2(fdout[1], 1) == -1) { |
| 3657 |      mg_cry(conn, |
| 3658 |       "%s: dup2(%d, 1): %s", |
| 3659 |       __func__, |
| 3660 |       fdout[1], |
| 3661 |       strerror(ERRNO)); |
| 3662 |    } else if (dup2(fderr[1], 2) == -1) { |
| 3663 |      mg_cry(conn, |
| 3664 |       "%s: dup2(%d, 2): %s", |
| 3665 |       __func__, |
| 3666 |       fderr[1], |
| 3667 |       strerror(ERRNO)); |
| 3668 |    } else { |
| 3669 |      /* Keep stderr and stdout in two different pipes. |
| 3670 |       * Stdout will be sent back to the client, |
| 3671 |       * stderr should go into a server error log. */ |
| 3672 |      (void)close(fdin[0]); |
| 3673 |      (void)close(fdout[1]); |
| 3674 |      (void)close(fderr[1]); |
| 3675 | |
| 3676 |      /* Close write end fdin and read end fdout and fderr */ |
| 3677 |      (void)close(fdin[1]); |
| 3678 |      (void)close(fdout[0]); |
| 3679 |      (void)close(fderr[0]); |
| 3680 | |
| 3681 |      /* After exec, all signal handlers are restored to their default |
| 3682 |       * values, with one exception of SIGCHLD. According to |
| 3683 |       * POSIX.1-2001 and Linux's implementation, SIGCHLD's handler will |
| 3684 |       * leave unchanged after exec if it was set to be ignored. Restore |
| 3685 |       * it to default action. */ |
| 3686 |      signal(SIGCHLD, SIG_DFL); |
| 3687 | |
| 3688 |      interp = conn->ctx->config[CGI_INTERPRETER]; |
| 3689 |      if (interp == NULL) { |
| 3690 |        (void)execle(prog, prog, NULL, envp); |
| 3691 |        mg_cry(conn, |
| 3692 |         "%s: execle(%s): %s", |
| 3693 |         __func__, |
| 3694 |         prog, |
| 3695 |         strerror(ERRNO)); |
| 3696 |      } else { |
| 3697 |        (void)execle(interp, interp, prog, NULL, envp); |
| 3698 |        mg_cry(conn, |
| 3699 |         "%s: execle(%s %s): %s", |
| 3700 |         __func__, |
| 3701 |         interp, |
| 3702 |         prog, |
| 3703 |         strerror(ERRNO)); |
| 3704 |      } |
| 3705 |    } |
| 3706 |    exit(EXIT_FAILURE); |
| 3707 |  } |
| 3708 | |
| 3709 |  return pid; |
| 3710 | } |
| 3711 | #endif /* !NO_CGI */ |
| 3712 | |
| 3713 | |
| 3714 | static int |
| 3715 | set_non_blocking_mode(SOCKET sock) |
| 3716 | { |
| 3717 |  int flags; |
| 3718 | |
| 3719 |  flags = fcntl(sock, F_GETFL, 0); |
| 3720 |  (void)fcntl(sock, F_SETFL, flags | O_NONBLOCK); |
| 3721 | |
| 3722 |  return 0; |
| 3723 | } |
| 3724 | #endif /* _WIN32 */ |
| 3725 | /* End of initial operating system specific define block. */ |
| 3726 | |
| 3727 | |
| 3728 | /* Get a random number (independent of C rand function) */ |
| 3729 | static uint64_t |
| 3730 | get_random(void) |
| 3731 | { |
| 3732 |  static uint64_t lfsr = 0; /* Linear feedback shift register */ |
| 3733 |  static uint64_t lcg = 0; /* Linear congruential generator */ |
| 3734 |  struct timespec now; |
| 3735 | |
| 3736 |  memset(&now, 0, sizeof(now)); |
| 3737 |  clock_gettime(CLOCK_MONOTONIC, &now); |
| 3738 | |
| 3739 |  if (lfsr == 0) { |
| 3740 |    /* lfsr will be only 0 if has not been initialized, |
| 3741 |     * so this code is called only once. */ |
| 3742 |    lfsr = (((uint64_t)now.tv_sec) << 21) ^ ((uint64_t)now.tv_nsec) |
| 3743 |     ^ ((uint64_t)(ptrdiff_t)&now) ^ (((uint64_t)time(NULL)) << 33); |
| 3744 |    lcg = (((uint64_t)now.tv_sec) << 25) + (uint64_t)now.tv_nsec |
| 3745 |     + (uint64_t)(ptrdiff_t)&now; |
| 3746 |  } else { |
| 3747 |    /* Get the next step of both random number generators. */ |
| 3748 |    lfsr = (lfsr >> 1) |
| 3749 |     | ((((lfsr >> 0) ^ (lfsr >> 1) ^ (lfsr >> 3) ^ (lfsr >> 4)) & 1) |
| 3750 |     << 63); |
| 3751 |    lcg = lcg * 6364136223846793005 + 1442695040888963407; |
| 3752 |  } |
| 3753 | |
| 3754 |  /* Combining two pseudo-random number generators and a high resolution part |
| 3755 |   * of the current server time will make it hard (impossible?) to guess the |
| 3756 |   * next number. */ |
| 3757 |  return (lfsr ^ lcg ^ (uint64_t)now.tv_nsec); |
| 3758 | } |
| 3759 | |
| 3760 | |
| 3761 | /* Write data to the IO channel - opened file descriptor, socket or SSL |
| 3762 | * descriptor. Return number of bytes written. */ |
| 3763 | static int |
| 3764 | push(struct mg_context *ctx, |
| 3765 | FILE *fp, |
| 3766 | SOCKET sock, |
| 3767 | SSL *ssl, |
| 3768 | const char *buf, |
| 3769 | int len, |
| 3770 | double timeout) |
| 3771 | { |
| 3772 |  struct timespec start, now; |
| 3773 |  int n, err; |
| 3774 | |
| 3775 | #ifdef _WIN32 |
| 3776 |  typedef int len_t; |
| 3777 | #else |
| 3778 |  typedef size_t len_t; |
| 3779 | #endif |
| 3780 | |
| 3781 |  if (timeout > 0) { |
| 3782 |    memset(&start, 0, sizeof(start)); |
| 3783 |    memset(&now, 0, sizeof(now)); |
| 3784 |    clock_gettime(CLOCK_MONOTONIC, &start); |
| 3785 |  } |
| 3786 | |
| 3787 |  if (ctx == NULL) { |
| 3788 |    return -1; |
| 3789 |  } |
| 3790 | |
| 3791 | #ifdef NO_SSL |
| 3792 |  if (ssl) { |
| 3793 |    return -1; |
| 3794 |  } |
| 3795 | #endif |
| 3796 | |
| 3797 |  do { |
| 3798 | |
| 3799 | #ifndef NO_SSL |
| 3800 |    if (ssl != NULL) { |
| 3801 |      n = SSL_write(ssl, buf, len); |
| 3802 |      if (n <= 0) { |
| 3803 |        err = SSL_get_error(ssl, n); |
| 3804 |        if ((err == 5 /* SSL_ERROR_SYSCALL */) && (n == -1)) { |
| 3805 |          err = ERRNO; |
| 3806 |        } else { |
| 3807 |          DEBUG_TRACE("SSL_write() failed, error %d", err); |
| 3808 |          return -1; |
| 3809 |        } |
| 3810 |      } else { |
| 3811 |        err = 0; |
| 3812 |      } |
| 3813 |    } else |
| 3814 | #endif |
| 3815 |     if (fp != NULL) { |
| 3816 |      n = (int)fwrite(buf, 1, (size_t)len, fp); |
| 3817 |      if (ferror(fp)) { |
| 3818 |        n = -1; |
| 3819 |        err = ERRNO; |
| 3820 |      } else { |
| 3821 |        err = 0; |
| 3822 |      } |
| 3823 |    } else { |
| 3824 |      n = (int)send(sock, buf, (len_t)len, MSG_NOSIGNAL); |
| 3825 |      err = (n < 0) ? ERRNO : 0; |
| 3826 |    } |
| 3827 | |
| 3828 |    if (ctx->stop_flag) { |
| 3829 |      return -1; |
| 3830 |    } |
| 3831 | |
| 3832 |    if ((n > 0) || (n == 0 && len == 0)) { |
| 3833 |      /* some data has been read, or no data was requested */ |
| 3834 |      return n; |
| 3835 |    } |
| 3836 |    if (n == 0) { |
| 3837 |      /* shutdown of the socket at client side */ |
| 3838 |      return -1; |
| 3839 |    } |
| 3840 |    if (n < 0) { |
| 3841 |      /* socket error - check errno */ |
| 3842 |      DEBUG_TRACE("send() failed, error %d", err); |
| 3843 | |
| 3844 |      /* TODO: error handling depending on the error code. |
| 3845 |       * These codes are different between Windows and Linux. |
| 3846 |       */ |
| 3847 |      return -1; |
| 3848 |    } |
| 3849 | |
| 3850 |    /* This code is not reached in the moment. |
| 3851 |     * ==> Fix the TODOs above first. */ |
| 3852 | |
| 3853 |    if (timeout > 0) { |
| 3854 |      clock_gettime(CLOCK_MONOTONIC, &now); |
| 3855 |    } |
| 3856 | |
| 3857 |  } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); |
| 3858 | |
| 3859 |  (void)err; /* Avoid unused warning if NO_SSL is set and DEBUG_TRACE is not |
| 3860 |   used */ |
| 3861 | |
| 3862 |  return -1; |
| 3863 | } |
| 3864 | |
| 3865 | |
| 3866 | static int64_t |
| 3867 | push_all(struct mg_context *ctx, |
| 3868 | FILE *fp, |
| 3869 | SOCKET sock, |
| 3870 | SSL *ssl, |
| 3871 | const char *buf, |
| 3872 | int64_t len) |
| 3873 | { |
| 3874 |  double timeout = -1.0; |
| 3875 |  int64_t n, nwritten = 0; |
| 3876 | |
| 3877 |  if (ctx == NULL) { |
| 3878 |    return -1; |
| 3879 |  } |
| 3880 | |
| 3881 |  if (ctx->config[REQUEST_TIMEOUT]) { |
| 3882 |    timeout = atoi(ctx->config[REQUEST_TIMEOUT]) / 1000.0; |
| 3883 |  } |
| 3884 | |
| 3885 |  while (len > 0 && ctx->stop_flag == 0) { |
| 3886 |    n = push(ctx, fp, sock, ssl, buf + nwritten, (int)len, timeout); |
| 3887 |    if (n < 0) { |
| 3888 |      if (nwritten == 0) { |
| 3889 |        nwritten = n; /* Propagate the error */ |
| 3890 |      } |
| 3891 |      break; |
| 3892 |    } else if (n == 0) { |
| 3893 |      break; /* No more data to write */ |
| 3894 |    } else { |
| 3895 |      nwritten += n; |
| 3896 |      len -= n; |
| 3897 |    } |
| 3898 |  } |
| 3899 | |
| 3900 |  return nwritten; |
| 3901 | } |
| 3902 | |
| 3903 | |
| 3904 | /* Read from IO channel - opened file descriptor, socket, or SSL descriptor. |
| 3905 | * Return negative value on error, or number of bytes read on success. */ |
| 3906 | static int |
| 3907 | pull(FILE *fp, struct mg_connection *conn, char *buf, int len, double timeout) |
| 3908 | { |
| 3909 |  int nread, err; |
| 3910 |  struct timespec start, now; |
| 3911 | |
| 3912 | #ifdef _WIN32 |
| 3913 |  typedef int len_t; |
| 3914 | #else |
| 3915 |  typedef size_t len_t; |
| 3916 | #endif |
| 3917 | |
| 3918 |  if (timeout > 0) { |
| 3919 |    memset(&start, 0, sizeof(start)); |
| 3920 |    memset(&now, 0, sizeof(now)); |
| 3921 |    clock_gettime(CLOCK_MONOTONIC, &start); |
| 3922 |  } |
| 3923 | |
| 3924 |  do { |
| 3925 |    if (fp != NULL) { |
| 3926 |      /* Use read() instead of fread(), because if we're reading from the |
| 3927 |       * CGI pipe, fread() may block until IO buffer is filled up. We |
| 3928 |       * cannot afford to block and must pass all read bytes immediately |
| 3929 |       * to the client. */ |
| 3930 |      nread = (int)read(fileno(fp), buf, (size_t)len); |
| 3931 |      err = (nread < 0) ? ERRNO : 0; |
| 3932 | |
| 3933 | #ifndef NO_SSL |
| 3934 |    } else if (conn->ssl != NULL) { |
| 3935 |      nread = SSL_read(conn->ssl, buf, len); |
| 3936 |      if (nread <= 0) { |
| 3937 |        err = SSL_get_error(conn->ssl, nread); |
| 3938 |        if ((err == 5 /* SSL_ERROR_SYSCALL */) && (nread == -1)) { |
| 3939 |          err = ERRNO; |
| 3940 |        } else { |
| 3941 |          DEBUG_TRACE("SSL_read() failed, error %d", err); |
| 3942 |          return -1; |
| 3943 |        } |
| 3944 |      } else { |
| 3945 |        err = 0; |
| 3946 |      } |
| 3947 | #endif |
| 3948 | |
| 3949 |    } else { |
| 3950 |      nread = (int)recv(conn->client.sock, buf, (len_t)len, 0); |
| 3951 |      err = (nread < 0) ? ERRNO : 0; |
| 3952 |    } |
| 3953 | |
| 3954 |    if (conn->ctx->stop_flag) { |
| 3955 |      return -1; |
| 3956 |    } |
| 3957 | |
| 3958 |    if ((nread > 0) || (nread == 0 && len == 0)) { |
| 3959 |      /* some data has been read, or no data was requested */ |
| 3960 |      return nread; |
| 3961 |    } |
| 3962 |    if (nread == 0) { |
| 3963 |      /* shutdown of the socket at client side */ |
| 3964 |      return -1; |
| 3965 |    } |
| 3966 |    if (nread < 0) { |
| 3967 | /* socket error - check errno */ |
| 3968 | #ifdef _WIN32 |
| 3969 |      if (err == WSAEWOULDBLOCK) { |
| 3970 |        /* standard case if called from close_socket_gracefully */ |
| 3971 |        return -1; |
| 3972 |      } else if (err == WSAETIMEDOUT) { |
| 3973 |        /* timeout is handled by the while loop */ |
| 3974 |      } else { |
| 3975 |        DEBUG_TRACE("recv() failed, error %d", err); |
| 3976 |        return -1; |
| 3977 |      } |
| 3978 | #else |
| 3979 |      /* TODO: POSIX returns either EAGAIN or EWOULDBLOCK in both cases, |
| 3980 |       * if the timeout is reached and if the socket was set to non- |
| 3981 |       * blocking in close_socket_gracefully, so we can not distinguish |
| 3982 |       * here. We have to wait for the timeout in both cases for now. |
| 3983 |       */ |
| 3984 |      if (err == EAGAIN || err == EWOULDBLOCK || err == EINTR) { |
| 3985 |        /* EAGAIN/EWOULDBLOCK: |
| 3986 |         * standard case if called from close_socket_gracefully |
| 3987 |         * => should return -1 */ |
| 3988 |        /* or timeout occured |
| 3989 |         * => the code must stay in the while loop */ |
| 3990 | |
| 3991 |        /* EINTR can be generated on a socket with a timeout set even |
| 3992 |         * when SA_RESTART is effective for all relevant signals |
| 3993 |         * (see signal(7)). |
| 3994 |         * => stay in the while loop */ |
| 3995 |      } else { |
| 3996 |        DEBUG_TRACE("recv() failed, error %d", err); |
| 3997 |        return -1; |
| 3998 |      } |
| 3999 | #endif |
| 4000 |    } |
| 4001 |    if (timeout > 0) { |
| 4002 |      clock_gettime(CLOCK_MONOTONIC, &now); |
| 4003 |    } |
| 4004 |  } while ((timeout <= 0) || (mg_difftimespec(&now, &start) <= timeout)); |
| 4005 | |
| 4006 |  /* Timeout occured, but no data available. */ |
| 4007 |  return -1; |
| 4008 | } |
| 4009 | |
| 4010 | |
| 4011 | static int |
| 4012 | pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) |
| 4013 | { |
| 4014 |  int n, nread = 0; |
| 4015 |  double timeout = -1.0; |
| 4016 | |
| 4017 |  if (conn->ctx->config[REQUEST_TIMEOUT]) { |
| 4018 |    timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; |
| 4019 |  } |
| 4020 | |
| 4021 |  while (len > 0 && conn->ctx->stop_flag == 0) { |
| 4022 |    n = pull(fp, conn, buf + nread, len, timeout); |
| 4023 |    if (n < 0) { |
| 4024 |      if (nread == 0) { |
| 4025 |        nread = n; /* Propagate the error */ |
| 4026 |      } |
| 4027 |      break; |
| 4028 |    } else if (n == 0) { |
| 4029 |      break; /* No more data to read */ |
| 4030 |    } else { |
| 4031 |      conn->consumed_content += n; |
| 4032 |      nread += n; |
| 4033 |      len -= n; |
| 4034 |    } |
| 4035 |  } |
| 4036 | |
| 4037 |  return nread; |
| 4038 | } |
| 4039 | |
| 4040 | |
| 4041 | static void |
| 4042 | discard_unread_request_data(struct mg_connection *conn) |
| 4043 | { |
| 4044 |  char buf[MG_BUF_LEN]; |
| 4045 |  size_t to_read; |
| 4046 |  int nread; |
| 4047 | |
| 4048 |  if (conn == NULL) { |
| 4049 |    return; |
| 4050 |  } |
| 4051 | |
| 4052 |  to_read = sizeof(buf); |
| 4053 | |
| 4054 |  if (conn->is_chunked) { |
| 4055 |    /* Chunked encoding: 1=chunk not read completely, 2=chunk read |
| 4056 |     * completely */ |
| 4057 |    while (conn->is_chunked == 1) { |
| 4058 |      nread = mg_read(conn, buf, to_read); |
| 4059 |      if (nread <= 0) { |
| 4060 |        break; |
| 4061 |      } |
| 4062 |    } |
| 4063 | |
| 4064 |  } else { |
| 4065 |    /* Not chunked: content length is known */ |
| 4066 |    while (conn->consumed_content < conn->content_len) { |
| 4067 |      if (to_read |
| 4068 |       > (size_t)(conn->content_len - conn->consumed_content)) { |
| 4069 |        to_read = (size_t)(conn->content_len - conn->consumed_content); |
| 4070 |      } |
| 4071 | |
| 4072 |      nread = mg_read(conn, buf, to_read); |
| 4073 |      if (nread <= 0) { |
| 4074 |        break; |
| 4075 |      } |
| 4076 |    } |
| 4077 |  } |
| 4078 | } |
| 4079 | |
| 4080 | |
| 4081 | static int |
| 4082 | mg_read_inner(struct mg_connection *conn, void *buf, size_t len) |
| 4083 | { |
| 4084 |  int64_t n, buffered_len, nread; |
| 4085 |  int64_t len64 = |
| 4086 |   (int64_t)(len > INT_MAX ? INT_MAX : len); /* since the return value is |
| 4087 |   * int, we may not read more |
| 4088 |   * bytes */ |
| 4089 |  const char *body; |
| 4090 | |
| 4091 |  if (conn == NULL) { |
| 4092 |    return 0; |
| 4093 |  } |
| 4094 | |
| 4095 |  /* If Content-Length is not set for a PUT or POST request, read until |
| 4096 |   * socket is closed */ |
| 4097 |  if (conn->consumed_content == 0 && conn->content_len == -1) { |
| 4098 |    conn->content_len = INT64_MAX; |
| 4099 |    conn->must_close = 1; |
| 4100 |  } |
| 4101 | |
| 4102 |  nread = 0; |
| 4103 |  if (conn->consumed_content < conn->content_len) { |
| 4104 |    /* Adjust number of bytes to read. */ |
| 4105 |    int64_t left_to_read = conn->content_len - conn->consumed_content; |
| 4106 |    if (left_to_read < len64) { |
| 4107 |      /* Do not read more than the total content length of the request. |
| 4108 |       */ |
| 4109 |      len64 = left_to_read; |
| 4110 |    } |
| 4111 | |
| 4112 |    /* Return buffered data */ |
| 4113 |    buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len |
| 4114 |     - conn->consumed_content; |
| 4115 |    if (buffered_len > 0) { |
| 4116 |      if (len64 < buffered_len) { |
| 4117 |        buffered_len = len64; |
| 4118 |      } |
| 4119 |      body = conn->buf + conn->request_len + conn->consumed_content; |
| 4120 |      memcpy(buf, body, (size_t)buffered_len); |
| 4121 |      len64 -= buffered_len; |
| 4122 |      conn->consumed_content += buffered_len; |
| 4123 |      nread += buffered_len; |
| 4124 |      buf = (char *)buf + buffered_len; |
| 4125 |    } |
| 4126 | |
| 4127 |    /* We have returned all buffered data. Read new data from the remote |
| 4128 |     * socket. |
| 4129 |     */ |
| 4130 |    if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) { |
| 4131 |      nread += n; |
| 4132 |    } else { |
| 4133 |      nread = (nread > 0 ? nread : n); |
| 4134 |    } |
| 4135 |  } |
| 4136 |  return (int)nread; |
| 4137 | } |
| 4138 | |
| 4139 | |
| 4140 | static char |
| 4141 | mg_getc(struct mg_connection *conn) |
| 4142 | { |
| 4143 |  char c; |
| 4144 |  if (conn == NULL) { |
| 4145 |    return 0; |
| 4146 |  } |
| 4147 |  conn->content_len++; |
| 4148 |  if (mg_read_inner(conn, &c, 1) <= 0) { |
| 4149 |    return (char)0; |
| 4150 |  } |
| 4151 |  return c; |
| 4152 | } |
| 4153 | |
| 4154 | |
| 4155 | int |
| 4156 | mg_read(struct mg_connection *conn, void *buf, size_t len) |
| 4157 | { |
| 4158 |  if (len > INT_MAX) { |
| 4159 |    len = INT_MAX; |
| 4160 |  } |
| 4161 | |
| 4162 |  if (conn == NULL) { |
| 4163 |    return 0; |
| 4164 |  } |
| 4165 | |
| 4166 |  if (conn->is_chunked) { |
| 4167 |    size_t all_read = 0; |
| 4168 | |
| 4169 |    while (len > 0) { |
| 4170 | |
| 4171 |      if (conn->is_chunked == 2) { |
| 4172 |        /* No more data left to read */ |
| 4173 |        return 0; |
| 4174 |      } |
| 4175 | |
| 4176 |      if (conn->chunk_remainder) { |
| 4177 |        /* copy from the remainder of the last received chunk */ |
| 4178 |        long read_ret; |
| 4179 |        size_t read_now = |
| 4180 |         ((conn->chunk_remainder > len) ? (len) |
| 4181 |         : (conn->chunk_remainder)); |
| 4182 | |
| 4183 |        conn->content_len += (int)read_now; |
| 4184 |        read_ret = |
| 4185 |         mg_read_inner(conn, (char *)buf + all_read, read_now); |
| 4186 |        all_read += (size_t)read_ret; |
| 4187 | |
| 4188 |        conn->chunk_remainder -= read_now; |
| 4189 |        len -= read_now; |
| 4190 | |
| 4191 |        if (conn->chunk_remainder == 0) { |
| 4192 |          /* the rest of the data in the current chunk has been read |
| 4193 |           */ |
| 4194 |          if ((mg_getc(conn) != '\r') || (mg_getc(conn) != '\n')) { |
| 4195 |            /* Protocol violation */ |
| 4196 |            return -1; |
| 4197 |          } |
| 4198 |        } |
| 4199 | |
| 4200 |      } else { |
| 4201 |        /* fetch a new chunk */ |
| 4202 |        int i = 0; |
| 4203 |        char lenbuf[64]; |
| 4204 |        char *end = 0; |
| 4205 |        unsigned long chunkSize = 0; |
| 4206 | |
| 4207 |        for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) { |
| 4208 |          lenbuf[i] = mg_getc(conn); |
| 4209 |          if (i > 0 && lenbuf[i] == '\r' && lenbuf[i - 1] != '\r') { |
| 4210 |            continue; |
| 4211 |          } |
| 4212 |          if (i > 1 && lenbuf[i] == '\n' && lenbuf[i - 1] == '\r') { |
| 4213 |            lenbuf[i + 1] = 0; |
| 4214 |            chunkSize = strtoul(lenbuf, &end, 16); |
| 4215 |            if (chunkSize == 0) { |
| 4216 |              /* regular end of content */ |
| 4217 |              conn->is_chunked = 2; |
| 4218 |            } |
| 4219 |            break; |
| 4220 |          } |
| 4221 |          if (!isalnum(lenbuf[i])) { |
| 4222 |            /* illegal character for chunk length */ |
| 4223 |            return -1; |
| 4224 |          } |
| 4225 |        } |
| 4226 |        if ((end == NULL) || (*end != '\r')) { |
| 4227 |          /* chunksize not set correctly */ |
| 4228 |          return -1; |
| 4229 |        } |
| 4230 |        if (chunkSize == 0) { |
| 4231 |          break; |
| 4232 |        } |
| 4233 | |
| 4234 |        conn->chunk_remainder = chunkSize; |
| 4235 |      } |
| 4236 |    } |
| 4237 | |
| 4238 |    return (int)all_read; |
| 4239 |  } |
| 4240 |  return mg_read_inner(conn, buf, len); |
| 4241 | } |
| 4242 | |
| 4243 | |
| 4244 | int |
| 4245 | mg_write(struct mg_connection *conn, const void *buf, size_t len) |
| 4246 | { |
| 4247 |  time_t now; |
| 4248 |  int64_t n, total, allowed; |
| 4249 | |
| 4250 |  if (conn == NULL) { |
| 4251 |    return 0; |
| 4252 |  } |
| 4253 | |
| 4254 |  if (conn->throttle > 0) { |
| 4255 |    if ((now = time(NULL)) != conn->last_throttle_time) { |
| 4256 |      conn->last_throttle_time = now; |
| 4257 |      conn->last_throttle_bytes = 0; |
| 4258 |    } |
| 4259 |    allowed = conn->throttle - conn->last_throttle_bytes; |
| 4260 |    if (allowed > (int64_t)len) { |
| 4261 |      allowed = (int64_t)len; |
| 4262 |    } |
| 4263 |    if ((total = push_all(conn->ctx, |
| 4264 |     NULL, |
| 4265 |     conn->client.sock, |
| 4266 |     conn->ssl, |
| 4267 |     (const char *)buf, |
| 4268 |     (int64_t)allowed)) == allowed) { |
| 4269 |      buf = (const char *)buf + total; |
| 4270 |      conn->last_throttle_bytes += total; |
| 4271 |      while (total < (int64_t)len && conn->ctx->stop_flag == 0) { |
| 4272 |        allowed = conn->throttle > (int64_t)len - total |
| 4273 |         ? (int64_t)len - total |
| 4274 |         : conn->throttle; |
| 4275 |        if ((n = push_all(conn->ctx, |
| 4276 |         NULL, |
| 4277 |         conn->client.sock, |
| 4278 |         conn->ssl, |
| 4279 |         (const char *)buf, |
| 4280 |         (int64_t)allowed)) != allowed) { |
| 4281 |          break; |
| 4282 |        } |
| 4283 |        sleep(1); |
| 4284 |        conn->last_throttle_bytes = allowed; |
| 4285 |        conn->last_throttle_time = time(NULL); |
| 4286 |        buf = (const char *)buf + n; |
| 4287 |        total += n; |
| 4288 |      } |
| 4289 |    } |
| 4290 |  } else { |
| 4291 |    total = push_all(conn->ctx, |
| 4292 |     NULL, |
| 4293 |     conn->client.sock, |
| 4294 |     conn->ssl, |
| 4295 |     (const char *)buf, |
| 4296 |     (int64_t)len); |
| 4297 |  } |
| 4298 |  return (int)total; |
| 4299 | } |
| 4300 | |
| 4301 | |
| 4302 | /* Alternative alloc_vprintf() for non-compliant C runtimes */ |
| 4303 | static int |
| 4304 | alloc_vprintf2(char **buf, const char *fmt, va_list ap) |
| 4305 | { |
| 4306 |  va_list ap_copy; |
| 4307 |  size_t size = MG_BUF_LEN / 4; |
| 4308 |  int len = -1; |
| 4309 | |
| 4310 |  *buf = NULL; |
| 4311 |  while (len < 0) { |
| 4312 |    if (*buf) { |
| 4313 |      mg_free(*buf); |
| 4314 |    } |
| 4315 | |
| 4316 |    size *= 4; |
| 4317 |    *buf = (char *)mg_malloc(size); |
| 4318 |    if (!*buf) { |
| 4319 |      break; |
| 4320 |    } |
| 4321 | |
| 4322 |    va_copy(ap_copy, ap); |
| 4323 |    len = vsnprintf_impl(*buf, size - 1, fmt, ap_copy); |
| 4324 |    va_end(ap_copy); |
| 4325 |    (*buf)[size - 1] = 0; |
| 4326 |  } |
| 4327 | |
| 4328 |  return len; |
| 4329 | } |
| 4330 | |
| 4331 | |
| 4332 | /* Print message to buffer. If buffer is large enough to hold the message, |
| 4333 | * return buffer. If buffer is to small, allocate large enough buffer on heap, |
| 4334 | * and return allocated buffer. */ |
| 4335 | static int |
| 4336 | alloc_vprintf(char **out_buf, |
| 4337 | char *prealloc_buf, |
| 4338 | size_t prealloc_size, |
| 4339 | const char *fmt, |
| 4340 | va_list ap) |
| 4341 | { |
| 4342 |  va_list ap_copy; |
| 4343 |  int len; |
| 4344 | |
| 4345 |  /* Windows is not standard-compliant, and vsnprintf() returns -1 if |
| 4346 |   * buffer is too small. Also, older versions of msvcrt.dll do not have |
| 4347 |   * _vscprintf(). However, if size is 0, vsnprintf() behaves correctly. |
| 4348 |   * Therefore, we make two passes: on first pass, get required message |
| 4349 |   * length. |
| 4350 |   * On second pass, actually print the message. */ |
| 4351 |  va_copy(ap_copy, ap); |
| 4352 |  len = vsnprintf_impl(NULL, 0, fmt, ap_copy); |
| 4353 |  va_end(ap_copy); |
| 4354 | |
| 4355 |  if (len < 0) { |
| 4356 |    /* C runtime is not standard compliant, vsnprintf() returned -1. |
| 4357 |     * Switch to alternative code path that uses incremental allocations. |
| 4358 |    */ |
| 4359 |    va_copy(ap_copy, ap); |
| 4360 |    len = alloc_vprintf2(out_buf, fmt, ap); |
| 4361 |    va_end(ap_copy); |
| 4362 | |
| 4363 |  } else if ((size_t)(len) >= prealloc_size) { |
| 4364 |    /* The pre-allocated buffer not large enough. */ |
| 4365 |    /* Allocate a new buffer. */ |
| 4366 |    *out_buf = (char *)mg_malloc((size_t)(len) + 1); |
| 4367 |    if (!*out_buf) { |
| 4368 |      /* Allocation failed. Return -1 as "out of memory" error. */ |
| 4369 |      return -1; |
| 4370 |    } |
| 4371 |    /* Buffer allocation successful. Store the string there. */ |
| 4372 |    va_copy(ap_copy, ap); |
| 4373 |    IGNORE_UNUSED_RESULT( |
| 4374 |     vsnprintf_impl(*out_buf, (size_t)(len) + 1, fmt, ap_copy)); |
| 4375 |    va_end(ap_copy); |
| 4376 | |
| 4377 |  } else { |
| 4378 |    /* The pre-allocated buffer is large enough. |
| 4379 |     * Use it to store the string and return the address. */ |
| 4380 |    va_copy(ap_copy, ap); |
| 4381 |    IGNORE_UNUSED_RESULT( |
| 4382 |     vsnprintf_impl(prealloc_buf, prealloc_size, fmt, ap_copy)); |
| 4383 |    va_end(ap_copy); |
| 4384 |    *out_buf = prealloc_buf; |
| 4385 |  } |
| 4386 | |
| 4387 |  return len; |
| 4388 | } |
| 4389 | |
| 4390 | |
| 4391 | static int |
| 4392 | mg_vprintf(struct mg_connection *conn, const char *fmt, va_list ap) |
| 4393 | { |
| 4394 |  char mem[MG_BUF_LEN]; |
| 4395 |  char *buf = NULL; |
| 4396 |  int len; |
| 4397 | |
| 4398 |  if ((len = alloc_vprintf(&buf, mem, sizeof(mem), fmt, ap)) > 0) { |
| 4399 |    len = mg_write(conn, buf, (size_t)len); |
| 4400 |  } |
| 4401 |  if (buf != mem && buf != NULL) { |
| 4402 |    mg_free(buf); |
| 4403 |  } |
| 4404 | |
| 4405 |  return len; |
| 4406 | } |
| 4407 | |
| 4408 | |
| 4409 | int |
| 4410 | mg_printf(struct mg_connection *conn, const char *fmt, ...) |
| 4411 | { |
| 4412 |  va_list ap; |
| 4413 |  int result; |
| 4414 | |
| 4415 |  va_start(ap, fmt); |
| 4416 |  result = mg_vprintf(conn, fmt, ap); |
| 4417 |  va_end(ap); |
| 4418 | |
| 4419 |  return result; |
| 4420 | } |
| 4421 | |
| 4422 | |
| 4423 | int |
| 4424 | mg_url_decode(const char *src, |
| 4425 | int src_len, |
| 4426 | char *dst, |
| 4427 | int dst_len, |
| 4428 | int is_form_url_encoded) |
| 4429 | { |
| 4430 |  int i, j, a, b; |
| 4431 | #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') |
| 4432 | |
| 4433 |  for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++) { |
| 4434 |    if (i < src_len - 2 && src[i] == '%' |
| 4435 |     && isxdigit(*(const unsigned char *)(src + i + 1)) |
| 4436 |     && isxdigit(*(const unsigned char *)(src + i + 2))) { |
| 4437 |      a = tolower(*(const unsigned char *)(src + i + 1)); |
| 4438 |      b = tolower(*(const unsigned char *)(src + i + 2)); |
| 4439 |      dst[j] = (char)((HEXTOI(a) << 4) | HEXTOI(b)); |
| 4440 |      i += 2; |
| 4441 |    } else if (is_form_url_encoded && src[i] == '+') { |
| 4442 |      dst[j] = ' '; |
| 4443 |    } else { |
| 4444 |      dst[j] = src[i]; |
| 4445 |    } |
| 4446 |  } |
| 4447 | |
| 4448 |  dst[j] = '\0'; /* Null-terminate the destination */ |
| 4449 | |
| 4450 |  return i >= src_len ? j : -1; |
| 4451 | } |
| 4452 | |
| 4453 | |
| 4454 | int |
| 4455 | mg_get_var(const char *data, |
| 4456 | size_t data_len, |
| 4457 | const char *name, |
| 4458 | char *dst, |
| 4459 | size_t dst_len) |
| 4460 | { |
| 4461 |  return mg_get_var2(data, data_len, name, dst, dst_len, 0); |
| 4462 | } |
| 4463 | |
| 4464 | |
| 4465 | int |
| 4466 | mg_get_var2(const char *data, |
| 4467 | size_t data_len, |
| 4468 | const char *name, |
| 4469 | char *dst, |
| 4470 | size_t dst_len, |
| 4471 | size_t occurrence) |
| 4472 | { |
| 4473 |  const char *p, *e, *s; |
| 4474 |  size_t name_len; |
| 4475 |  int len; |
| 4476 | |
| 4477 |  if (dst == NULL || dst_len == 0) { |
| 4478 |    len = -2; |
| 4479 |  } else if (data == NULL || name == NULL || data_len == 0) { |
| 4480 |    len = -1; |
| 4481 |    dst[0] = '\0'; |
| 4482 |  } else { |
| 4483 |    name_len = strlen(name); |
| 4484 |    e = data + data_len; |
| 4485 |    len = -1; |
| 4486 |    dst[0] = '\0'; |
| 4487 | |
| 4488 |    /* data is "var1=val1&var2=val2...". Find variable first */ |
| 4489 |    for (p = data; p + name_len < e; p++) { |
| 4490 |      if ((p == data || p[-1] == '&') && p[name_len] == '=' |
| 4491 |       && !mg_strncasecmp(name, p, name_len) && 0 == occurrence--) { |
| 4492 |        /* Point p to variable value */ |
| 4493 |        p += name_len + 1; |
| 4494 | |
| 4495 |        /* Point s to the end of the value */ |
| 4496 |        s = (const char *)memchr(p, '&', (size_t)(e - p)); |
| 4497 |        if (s == NULL) { |
| 4498 |          s = e; |
| 4499 |        } |
| 4500 |        /* assert(s >= p); */ |
| 4501 |        if (s < p) { |
| 4502 |          return -3; |
| 4503 |        } |
| 4504 | |
| 4505 |        /* Decode variable into destination buffer */ |
| 4506 |        len = mg_url_decode(p, (int)(s - p), dst, (int)dst_len, 1); |
| 4507 | |
| 4508 |        /* Redirect error code from -1 to -2 (destination buffer too |
| 4509 |         * small). */ |
| 4510 |        if (len == -1) { |
| 4511 |          len = -2; |
| 4512 |        } |
| 4513 |        break; |
| 4514 |      } |
| 4515 |    } |
| 4516 |  } |
| 4517 | |
| 4518 |  return len; |
| 4519 | } |
| 4520 | |
| 4521 | |
| 4522 | int |
| 4523 | mg_get_cookie(const char *cookie_header, |
| 4524 | const char *var_name, |
| 4525 | char *dst, |
| 4526 | size_t dst_size) |
| 4527 | { |
| 4528 |  const char *s, *p, *end; |
| 4529 |  int name_len, len = -1; |
| 4530 | |
| 4531 |  if (dst == NULL || dst_size == 0) { |
| 4532 |    len = -2; |
| 4533 |  } else if (var_name == NULL || (s = cookie_header) == NULL) { |
| 4534 |    len = -1; |
| 4535 |    dst[0] = '\0'; |
| 4536 |  } else { |
| 4537 |    name_len = (int)strlen(var_name); |
| 4538 |    end = s + strlen(s); |
| 4539 |    dst[0] = '\0'; |
| 4540 | |
| 4541 |    for (; (s = mg_strcasestr(s, var_name)) != NULL; s += name_len) { |
| 4542 |      if (s[name_len] == '=') { |
| 4543 |        s += name_len + 1; |
| 4544 |        if ((p = strchr(s, ' ')) == NULL) { |
| 4545 |          p = end; |
| 4546 |        } |
| 4547 |        if (p[-1] == ';') { |
| 4548 |          p--; |
| 4549 |        } |
| 4550 |        if (*s == '"' && p[-1] == '"' && p > s + 1) { |
| 4551 |          s++; |
| 4552 |          p--; |
| 4553 |        } |
| 4554 |        if ((size_t)(p - s) < dst_size) { |
| 4555 |          len = (int)(p - s); |
| 4556 |          mg_strlcpy(dst, s, (size_t)len + 1); |
| 4557 |        } else { |
| 4558 |          len = -3; |
| 4559 |        } |
| 4560 |        break; |
| 4561 |      } |
| 4562 |    } |
| 4563 |  } |
| 4564 |  return len; |
| 4565 | } |
| 4566 | |
| 4567 | |
| 4568 | #if defined(USE_WEBSOCKET) || defined(USE_LUA) |
| 4569 | static void |
| 4570 | base64_encode(const unsigned char *src, int src_len, char *dst) |
| 4571 | { |
| 4572 |  static const char *b64 = |
| 4573 |   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
| 4574 |  int i, j, a, b, c; |
| 4575 | |
| 4576 |  for (i = j = 0; i < src_len; i += 3) { |
| 4577 |    a = src[i]; |
| 4578 |    b = i + 1 >= src_len ? 0 : src[i + 1]; |
| 4579 |    c = i + 2 >= src_len ? 0 : src[i + 2]; |
| 4580 | |
| 4581 |    dst[j++] = b64[a >> 2]; |
| 4582 |    dst[j++] = b64[((a & 3) << 4) | (b >> 4)]; |
| 4583 |    if (i + 1 < src_len) { |
| 4584 |      dst[j++] = b64[(b & 15) << 2 | (c >> 6)]; |
| 4585 |    } |
| 4586 |    if (i + 2 < src_len) { |
| 4587 |      dst[j++] = b64[c & 63]; |
| 4588 |    } |
| 4589 |  } |
| 4590 |  while (j % 4 != 0) { |
| 4591 |    dst[j++] = '='; |
| 4592 |  } |
| 4593 |  dst[j++] = '\0'; |
| 4594 | } |
| 4595 | #endif |
| 4596 | |
| 4597 | |
| 4598 | #if defined(USE_LUA) |
| 4599 | static unsigned char |
| 4600 | b64reverse(char letter) |
| 4601 | { |
| 4602 |  if (letter >= 'A' && letter <= 'Z') { |
| 4603 |    return letter - 'A'; |
| 4604 |  } |
| 4605 |  if (letter >= 'a' && letter <= 'z') { |
| 4606 |    return letter - 'a' + 26; |
| 4607 |  } |
| 4608 |  if (letter >= '0' && letter <= '9') { |
| 4609 |    return letter - '0' + 52; |
| 4610 |  } |
| 4611 |  if (letter == '+') { |
| 4612 |    return 62; |
| 4613 |  } |
| 4614 |  if (letter == '/') { |
| 4615 |    return 63; |
| 4616 |  } |
| 4617 |  if (letter == '=') { |
| 4618 |    return 255; /* normal end */ |
| 4619 |  } |
| 4620 |  return 254; /* error */ |
| 4621 | } |
| 4622 | |
| 4623 | |
| 4624 | static int |
| 4625 | base64_decode(const unsigned char *src, int src_len, char *dst, size_t *dst_len) |
| 4626 | { |
| 4627 |  int i; |
| 4628 |  unsigned char a, b, c, d; |
| 4629 | |
| 4630 |  *dst_len = 0; |
| 4631 | |
| 4632 |  for (i = 0; i < src_len; i += 4) { |
| 4633 |    a = b64reverse(src[i]); |
| 4634 |    if (a >= 254) { |
| 4635 |      return i; |
| 4636 |    } |
| 4637 | |
| 4638 |    b = b64reverse(i + 1 >= src_len ? 0 : src[i + 1]); |
| 4639 |    if (b >= 254) { |
| 4640 |      return i + 1; |
| 4641 |    } |
| 4642 | |
| 4643 |    c = b64reverse(i + 2 >= src_len ? 0 : src[i + 2]); |
| 4644 |    if (c == 254) { |
| 4645 |      return i + 2; |
| 4646 |    } |
| 4647 | |
| 4648 |    d = b64reverse(i + 3 >= src_len ? 0 : src[i + 3]); |
| 4649 |    if (d == 254) { |
| 4650 |      return i + 3; |
| 4651 |    } |
| 4652 | |
| 4653 |    dst[(*dst_len)++] = (a << 2) + (b >> 4); |
| 4654 |    if (c != 255) { |
| 4655 |      dst[(*dst_len)++] = (b << 4) + (c >> 2); |
| 4656 |      if (d != 255) { |
| 4657 |        dst[(*dst_len)++] = (c << 6) + d; |
| 4658 |      } |
| 4659 |    } |
| 4660 |  } |
| 4661 |  return -1; |
| 4662 | } |
| 4663 | #endif |
| 4664 | |
| 4665 | |
| 4666 | static int |
| 4667 | is_put_or_delete_method(const struct mg_connection *conn) |
| 4668 | { |
| 4669 |  if (conn) { |
| 4670 |    const char *s = conn->request_info.request_method; |
| 4671 |    return s != NULL && (!strcmp(s, "PUT") || !strcmp(s, "DELETE") |
| 4672 |     || !strcmp(s, "MKCOL") || !strcmp(s, "PATCH")); |
| 4673 |  } |
| 4674 |  return 0; |
| 4675 | } |
| 4676 | |
| 4677 | |
| 4678 | static void |
| 4679 | interpret_uri(struct mg_connection *conn, /* in: request (must be valid) */ |
| 4680 | char *filename, /* out: filename */ |
| 4681 | size_t filename_buf_len, /* in: size of filename buffer */ |
| 4682 | struct file *filep, /* out: file structure */ |
| 4683 | int *is_found, /* out: file is found (directly) */ |
| 4684 | int *is_script_resource, /* out: handled by a script? */ |
| 4685 | int *is_websocket_request, /* out: websocket connetion? */ |
| 4686 | int *is_put_or_delete_request /* out: put/delete a file? */ |
| 4687 | ) |
| 4688 | { |
| 4689 | /* TODO (high): Restructure this function */ |
| 4690 | |
| 4691 | #if !defined(NO_FILES) |
| 4692 |  const char *uri = conn->request_info.local_uri; |
| 4693 |  const char *root = conn->ctx->config[DOCUMENT_ROOT]; |
| 4694 |  const char *rewrite; |
| 4695 |  struct vec a, b; |
| 4696 |  int match_len; |
| 4697 |  char gz_path[PATH_MAX]; |
| 4698 |  char const *accept_encoding; |
| 4699 |  int truncated; |
| 4700 | #if !defined(NO_CGI) || defined(USE_LUA) |
| 4701 |  char *p; |
| 4702 | #endif |
| 4703 | #else |
| 4704 |  (void)filename_buf_len; /* unused if NO_FILES is defined */ |
| 4705 | #endif |
| 4706 | |
| 4707 |  memset(filep, 0, sizeof(*filep)); |
| 4708 |  *filename = 0; |
| 4709 |  *is_found = 0; |
| 4710 |  *is_script_resource = 0; |
| 4711 |  *is_put_or_delete_request = is_put_or_delete_method(conn); |
| 4712 | |
| 4713 | #if defined(USE_WEBSOCKET) |
| 4714 |  *is_websocket_request = is_websocket_protocol(conn); |
| 4715 | #if !defined(NO_FILES) |
| 4716 |  if (*is_websocket_request && conn->ctx->config[WEBSOCKET_ROOT]) { |
| 4717 |    root = conn->ctx->config[WEBSOCKET_ROOT]; |
| 4718 |  } |
| 4719 | #endif /* !NO_FILES */ |
| 4720 | #else /* USE_WEBSOCKET */ |
| 4721 |  *is_websocket_request = 0; |
| 4722 | #endif /* USE_WEBSOCKET */ |
| 4723 | |
| 4724 | #if !defined(NO_FILES) |
| 4725 |  /* Note that root == NULL is a regular use case here. This occurs, |
| 4726 |   * if all requests are handled by callbacks, so the WEBSOCKET_ROOT |
| 4727 |   * config is not required. */ |
| 4728 |  if (root == NULL) { |
| 4729 |    /* all file related outputs have already been set to 0, just return |
| 4730 |     */ |
| 4731 |    return; |
| 4732 |  } |
| 4733 | |
| 4734 |  /* Using buf_len - 1 because memmove() for PATH_INFO may shift part |
| 4735 |   * of the path one byte on the right. |
| 4736 |   * If document_root is NULL, leave the file empty. */ |
| 4737 |  mg_snprintf( |
| 4738 |   conn, &truncated, filename, filename_buf_len - 1, "%s%s", root, uri); |
| 4739 | |
| 4740 |  if (truncated) { |
| 4741 |    goto interpret_cleanup; |
| 4742 |  } |
| 4743 | |
| 4744 |  rewrite = conn->ctx->config[REWRITE]; |
| 4745 |  while ((rewrite = next_option(rewrite, &a, &b)) != NULL) { |
| 4746 |    if ((match_len = match_prefix(a.ptr, a.len, uri)) > 0) { |
| 4747 |      mg_snprintf(conn, |
| 4748 |       &truncated, |
| 4749 |       filename, |
| 4750 |       filename_buf_len - 1, |
| 4751 |       "%.*s%s", |
| 4752 |       (int)b.len, |
| 4753 |       b.ptr, |
| 4754 |       uri + match_len); |
| 4755 |      break; |
| 4756 |    } |
| 4757 |  } |
| 4758 | |
| 4759 |  if (truncated) { |
| 4760 |    goto interpret_cleanup; |
| 4761 |  } |
| 4762 | |
| 4763 |  /* Local file path and name, corresponding to requested URI |
| 4764 |   * is now stored in "filename" variable. */ |
| 4765 |  if (mg_stat(conn, filename, filep)) { |
| 4766 | #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) |
| 4767 |    /* File exists. Check if it is a script type. */ |
| 4768 |    if (0 |
| 4769 | #if !defined(NO_CGI) |
| 4770 |     || match_prefix(conn->ctx->config[CGI_EXTENSIONS], |
| 4771 |     strlen(conn->ctx->config[CGI_EXTENSIONS]), |
| 4772 |     filename) > 0 |
| 4773 | #endif |
| 4774 | #if defined(USE_LUA) |
| 4775 |     || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], |
| 4776 |     strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), |
| 4777 |     filename) > 0 |
| 4778 | #endif |
| 4779 | #if defined(USE_DUKTAPE) |
| 4780 |     || match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], |
| 4781 |     strlen( |
| 4782 |     conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]), |
| 4783 |     filename) > 0 |
| 4784 | #endif |
| 4785 |     ) { |
| 4786 |      /* The request addresses a CGI script or a Lua script. The URI |
| 4787 |       * corresponds to the script itself (like /path/script.cgi), |
| 4788 |       * and there is no additional resource path |
| 4789 |       * (like /path/script.cgi/something). |
| 4790 |       * Requests that modify (replace or delete) a resource, like |
| 4791 |       * PUT and DELETE requests, should replace/delete the script |
| 4792 |       * file. |
| 4793 |       * Requests that read or write from/to a resource, like GET and |
| 4794 |       * POST requests, should call the script and return the |
| 4795 |       * generated response. */ |
| 4796 |      *is_script_resource = !*is_put_or_delete_request; |
| 4797 |    } |
| 4798 | #endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ |
| 4799 |    *is_found = 1; |
| 4800 |    return; |
| 4801 |  } |
| 4802 | |
| 4803 |  /* If we can't find the actual file, look for the file |
| 4804 |   * with the same name but a .gz extension. If we find it, |
| 4805 |   * use that and set the gzipped flag in the file struct |
| 4806 |   * to indicate that the response need to have the content- |
| 4807 |   * encoding: gzip header. |
| 4808 |   * We can only do this if the browser declares support. */ |
| 4809 |  if ((accept_encoding = mg_get_header(conn, "Accept-Encoding")) != NULL) { |
| 4810 |    if (strstr(accept_encoding, "gzip") != NULL) { |
| 4811 |      mg_snprintf( |
| 4812 |       conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", filename); |
| 4813 | |
| 4814 |      if (truncated) { |
| 4815 |        goto interpret_cleanup; |
| 4816 |      } |
| 4817 | |
| 4818 |      if (mg_stat(conn, gz_path, filep)) { |
| 4819 |        if (filep) { |
| 4820 |          filep->gzipped = 1; |
| 4821 |          *is_found = 1; |
| 4822 |        } |
| 4823 |        /* Currently gz files can not be scripts. */ |
| 4824 |        return; |
| 4825 |      } |
| 4826 |    } |
| 4827 |  } |
| 4828 | |
| 4829 | #if !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) |
| 4830 |  /* Support PATH_INFO for CGI scripts. */ |
| 4831 |  for (p = filename + strlen(filename); p > filename + 1; p--) { |
| 4832 |    if (*p == '/') { |
| 4833 |      *p = '\0'; |
| 4834 |      if ((0 |
| 4835 | #if !defined(NO_CGI) |
| 4836 |       || match_prefix(conn->ctx->config[CGI_EXTENSIONS], |
| 4837 |       strlen(conn->ctx->config[CGI_EXTENSIONS]), |
| 4838 |       filename) > 0 |
| 4839 | #endif |
| 4840 | #if defined(USE_LUA) |
| 4841 |       || match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], |
| 4842 |       strlen( |
| 4843 |       conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), |
| 4844 |       filename) > 0 |
| 4845 | #endif |
| 4846 | #if defined(USE_DUKTAPE) |
| 4847 |       || match_prefix( |
| 4848 |       conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], |
| 4849 |       strlen(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]), |
| 4850 |       filename) > 0 |
| 4851 | #endif |
| 4852 |       ) && mg_stat(conn, filename, filep)) { |
| 4853 |        /* Shift PATH_INFO block one character right, e.g. |
| 4854 |         * "/x.cgi/foo/bar\x00" => "/x.cgi\x00/foo/bar\x00" |
| 4855 |         * conn->path_info is pointing to the local variable "path" |
| 4856 |         * declared in handle_request(), so PATH_INFO is not valid |
| 4857 |         * after handle_request returns. */ |
| 4858 |        conn->path_info = p + 1; |
| 4859 |        memmove(p + 2, p + 1, strlen(p + 1) + 1); /* +1 is for |
| 4860 |         * trailing \0 */ |
| 4861 |        p[1] = '/'; |
| 4862 |        *is_script_resource = 1; |
| 4863 |        break; |
| 4864 |      } else { |
| 4865 |        *p = '/'; |
| 4866 |      } |
| 4867 |    } |
| 4868 |  } |
| 4869 | #endif /* !defined(NO_CGI) || defined(USE_LUA) || defined(USE_DUKTAPE) */ |
| 4870 | #endif /* !defined(NO_FILES) */ |
| 4871 |  return; |
| 4872 | |
| 4873 | #if !defined(NO_FILES) |
| 4874 | /* Reset all outputs */ |
| 4875 | interpret_cleanup: |
| 4876 |  memset(filep, 0, sizeof(*filep)); |
| 4877 |  *filename = 0; |
| 4878 |  *is_found = 0; |
| 4879 |  *is_script_resource = 0; |
| 4880 |  *is_websocket_request = 0; |
| 4881 |  *is_put_or_delete_request = 0; |
| 4882 | #endif /* !defined(NO_FILES) */ |
| 4883 | } |
| 4884 | |
| 4885 | |
| 4886 | /* Check whether full request is buffered. Return: |
| 4887 | * -1 if request is malformed |
| 4888 | * 0 if request is not yet fully buffered |
| 4889 | * >0 actual request length, including last \r\n\r\n */ |
| 4890 | static int |
| 4891 | get_request_len(const char *buf, int buflen) |
| 4892 | { |
| 4893 |  const char *s, *e; |
| 4894 |  int len = 0; |
| 4895 | |
| 4896 |  for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) |
| 4897 |    /* Control characters are not allowed but >=128 is. */ |
| 4898 |    if (!isprint(*(const unsigned char *)s) && *s != '\r' && *s != '\n' |
| 4899 |     && *(const unsigned char *)s < 128) { |
| 4900 |      len = -1; |
| 4901 |      break; /* [i_a] abort scan as soon as one malformed character is |
| 4902 |       * found; */ |
| 4903 |      /* don't let subsequent \r\n\r\n win us over anyhow */ |
| 4904 |    } else if (s[0] == '\n' && s[1] == '\n') { |
| 4905 |      len = (int)(s - buf) + 2; |
| 4906 |    } else if (s[0] == '\n' && &s[1] < e && s[1] == '\r' && s[2] == '\n') { |
| 4907 |      len = (int)(s - buf) + 3; |
| 4908 |    } |
| 4909 | |
| 4910 |  return len; |
| 4911 | } |
| 4912 | |
| 4913 | |
| 4914 | #if !defined(NO_CACHING) |
| 4915 | /* Convert month to the month number. Return -1 on error, or month number */ |
| 4916 | static int |
| 4917 | get_month_index(const char *s) |
| 4918 | { |
| 4919 |  size_t i; |
| 4920 | |
| 4921 |  for (i = 0; i < ARRAY_SIZE(month_names); i++) { |
| 4922 |    if (!strcmp(s, month_names[i])) { |
| 4923 |      return (int)i; |
| 4924 |    } |
| 4925 |  } |
| 4926 | |
| 4927 |  return -1; |
| 4928 | } |
| 4929 | |
| 4930 | |
| 4931 | /* Parse UTC date-time string, and return the corresponding time_t value. */ |
| 4932 | static time_t |
| 4933 | parse_date_string(const char *datetime) |
| 4934 | { |
| 4935 |  char month_str[32] = {0}; |
| 4936 |  int second, minute, hour, day, month, year; |
| 4937 |  time_t result = (time_t)0; |
| 4938 |  struct tm tm; |
| 4939 | |
| 4940 |  if ((sscanf(datetime, |
| 4941 |   "%d/%3s/%d %d:%d:%d", |
| 4942 |   &day, |
| 4943 |   month_str, |
| 4944 |   &year, |
| 4945 |   &hour, |
| 4946 |   &minute, |
| 4947 |   &second) == 6) || (sscanf(datetime, |
| 4948 |   "%d %3s %d %d:%d:%d", |
| 4949 |   &day, |
| 4950 |   month_str, |
| 4951 |   &year, |
| 4952 |   &hour, |
| 4953 |   &minute, |
| 4954 |   &second) == 6) |
| 4955 |   || (sscanf(datetime, |
| 4956 |   "%*3s, %d %3s %d %d:%d:%d", |
| 4957 |   &day, |
| 4958 |   month_str, |
| 4959 |   &year, |
| 4960 |   &hour, |
| 4961 |   &minute, |
| 4962 |   &second) == 6) || (sscanf(datetime, |
| 4963 |   "%d-%3s-%d %d:%d:%d", |
| 4964 |   &day, |
| 4965 |   month_str, |
| 4966 |   &year, |
| 4967 |   &hour, |
| 4968 |   &minute, |
| 4969 |   &second) == 6)) { |
| 4970 |    month = get_month_index(month_str); |
| 4971 |    if ((month >= 0) && (year >= 1970)) { |
| 4972 |      memset(&tm, 0, sizeof(tm)); |
| 4973 |      tm.tm_year = year - 1900; |
| 4974 |      tm.tm_mon = month; |
| 4975 |      tm.tm_mday = day; |
| 4976 |      tm.tm_hour = hour; |
| 4977 |      tm.tm_min = minute; |
| 4978 |      tm.tm_sec = second; |
| 4979 |      result = timegm(&tm); |
| 4980 |    } |
| 4981 |  } |
| 4982 | |
| 4983 |  return result; |
| 4984 | } |
| 4985 | #endif /* !NO_CACHING */ |
| 4986 | |
| 4987 | |
| 4988 | /* Protect against directory disclosure attack by removing '..', |
| 4989 | * excessive '/' and '\' characters */ |
| 4990 | static void |
| 4991 | remove_double_dots_and_double_slashes(char *s) |
| 4992 | { |
| 4993 |  char *p = s; |
| 4994 | |
| 4995 |  while (*s != '\0') { |
| 4996 |    *p++ = *s++; |
| 4997 |    if (s[-1] == '/' || s[-1] == '\\') { |
| 4998 |      /* Skip all following slashes, backslashes and double-dots */ |
| 4999 |      while (s[0] != '\0') { |
| 5000 |        if (s[0] == '/' || s[0] == '\\') { |
| 5001 |          s++; |
| 5002 |        } else if (s[0] == '.' && s[1] == '.') { |
| 5003 |          s += 2; |
| 5004 |        } else { |
| 5005 |          break; |
| 5006 |        } |
| 5007 |      } |
| 5008 |    } |
| 5009 |  } |
| 5010 |  *p = '\0'; |
| 5011 | } |
| 5012 | |
| 5013 | |
| 5014 | static const struct { |
| 5015 |  const char *extension; |
| 5016 |  size_t ext_len; |
| 5017 |  const char *mime_type; |
| 5018 | } builtin_mime_types[] = { |
| 5019 | /* IANA registered MIME types (http://www.iana.org/assignments/media-types) |
| 5020 | * application types */ |
| 5021 | {".doc", 4, "application/msword"}, |
| 5022 | {".eps", 4, "application/postscript"}, |
| 5023 | {".exe", 4, "application/octet-stream"}, |
| 5024 | {".js", 3, "application/javascript"}, |
| 5025 | {".json", 5, "application/json"}, |
| 5026 | {".pdf", 4, "application/pdf"}, |
| 5027 | {".ps", 3, "application/postscript"}, |
| 5028 | {".rtf", 4, "application/rtf"}, |
| 5029 | {".xhtml", 6, "application/xhtml+xml"}, |
| 5030 | {".xsl", 4, "application/xml"}, |
| 5031 | {".xslt", 5, "application/xml"}, |
| 5032 | |
| 5033 | /* fonts */ |
| 5034 | {".ttf", 4, "application/font-sfnt"}, |
| 5035 | {".cff", 4, "application/font-sfnt"}, |
| 5036 | {".otf", 4, "application/font-sfnt"}, |
| 5037 | {".aat", 4, "application/font-sfnt"}, |
| 5038 | {".sil", 4, "application/font-sfnt"}, |
| 5039 | {".pfr", 4, "application/font-tdpfr"}, |
| 5040 | {".woff", 5, "application/font-woff"}, |
| 5041 | |
| 5042 | /* audio */ |
| 5043 | {".mp3", 4, "audio/mpeg"}, |
| 5044 | {".oga", 4, "audio/ogg"}, |
| 5045 | {".ogg", 4, "audio/ogg"}, |
| 5046 | |
| 5047 | /* image */ |
| 5048 | {".gif", 4, "image/gif"}, |
| 5049 | {".ief", 4, "image/ief"}, |
| 5050 | {".jpeg", 5, "image/jpeg"}, |
| 5051 | {".jpg", 4, "image/jpeg"}, |
| 5052 | {".jpm", 4, "image/jpm"}, |
| 5053 | {".jpx", 4, "image/jpx"}, |
| 5054 | {".png", 4, "image/png"}, |
| 5055 | {".svg", 4, "image/svg+xml"}, |
| 5056 | {".tif", 4, "image/tiff"}, |
| 5057 | {".tiff", 5, "image/tiff"}, |
| 5058 | |
| 5059 | /* model */ |
| 5060 | {".wrl", 4, "model/vrml"}, |
| 5061 | |
| 5062 | /* text */ |
| 5063 | {".css", 4, "text/css"}, |
| 5064 | {".csv", 4, "text/csv"}, |
| 5065 | {".htm", 4, "text/html"}, |
| 5066 | {".html", 5, "text/html"}, |
| 5067 | {".sgm", 4, "text/sgml"}, |
| 5068 | {".shtm", 5, "text/html"}, |
| 5069 | {".shtml", 6, "text/html"}, |
| 5070 | {".txt", 4, "text/plain"}, |
| 5071 | {".xml", 4, "text/xml"}, |
| 5072 | |
| 5073 | /* video */ |
| 5074 | {".mov", 4, "video/quicktime"}, |
| 5075 | {".mp4", 4, "video/mp4"}, |
| 5076 | {".mpeg", 5, "video/mpeg"}, |
| 5077 | {".mpg", 4, "video/mpeg"}, |
| 5078 | {".ogv", 4, "video/ogg"}, |
| 5079 | {".qt", 3, "video/quicktime"}, |
| 5080 | |
| 5081 | /* not registered types |
| 5082 | * (http://reference.sitepoint.com/html/mime-types-full, |
| 5083 | * http://www.hansenb.pdx.edu/DMKB/dict/tutorials/mime_typ.php, ..) */ |
| 5084 | {".arj", 4, "application/x-arj-compressed"}, |
| 5085 | {".gz", 3, "application/x-gunzip"}, |
| 5086 | {".rar", 4, "application/x-arj-compressed"}, |
| 5087 | {".swf", 4, "application/x-shockwave-flash"}, |
| 5088 | {".tar", 4, "application/x-tar"}, |
| 5089 | {".tgz", 4, "application/x-tar-gz"}, |
| 5090 | {".torrent", 8, "application/x-bittorrent"}, |
| 5091 | {".ppt", 4, "application/x-mspowerpoint"}, |
| 5092 | {".xls", 4, "application/x-msexcel"}, |
| 5093 | {".zip", 4, "application/x-zip-compressed"}, |
| 5094 | {".aac", |
| 5095 | 4, |
| 5096 | "audio/aac"}, /* http://en.wikipedia.org/wiki/Advanced_Audio_Coding */ |
| 5097 | {".aif", 4, "audio/x-aif"}, |
| 5098 | {".m3u", 4, "audio/x-mpegurl"}, |
| 5099 | {".mid", 4, "audio/x-midi"}, |
| 5100 | {".ra", 3, "audio/x-pn-realaudio"}, |
| 5101 | {".ram", 4, "audio/x-pn-realaudio"}, |
| 5102 | {".wav", 4, "audio/x-wav"}, |
| 5103 | {".bmp", 4, "image/bmp"}, |
| 5104 | {".ico", 4, "image/x-icon"}, |
| 5105 | {".pct", 4, "image/x-pct"}, |
| 5106 | {".pict", 5, "image/pict"}, |
| 5107 | {".rgb", 4, "image/x-rgb"}, |
| 5108 | {".webm", 5, "video/webm"}, /* http://en.wikipedia.org/wiki/WebM */ |
| 5109 | {".asf", 4, "video/x-ms-asf"}, |
| 5110 | {".avi", 4, "video/x-msvideo"}, |
| 5111 | {".m4v", 4, "video/x-m4v"}, |
| 5112 | {NULL, 0, NULL}}; |
| 5113 | |
| 5114 | |
| 5115 | const char * |
| 5116 | mg_get_builtin_mime_type(const char *path) |
| 5117 | { |
| 5118 |  const char *ext; |
| 5119 |  size_t i, path_len; |
| 5120 | |
| 5121 |  path_len = strlen(path); |
| 5122 | |
| 5123 |  for (i = 0; builtin_mime_types[i].extension != NULL; i++) { |
| 5124 |    ext = path + (path_len - builtin_mime_types[i].ext_len); |
| 5125 |    if (path_len > builtin_mime_types[i].ext_len |
| 5126 |     && mg_strcasecmp(ext, builtin_mime_types[i].extension) == 0) { |
| 5127 |      return builtin_mime_types[i].mime_type; |
| 5128 |    } |
| 5129 |  } |
| 5130 | |
| 5131 |  return "text/plain"; |
| 5132 | } |
| 5133 | |
| 5134 | |
| 5135 | /* Look at the "path" extension and figure what mime type it has. |
| 5136 | * Store mime type in the vector. */ |
| 5137 | static void |
| 5138 | get_mime_type(struct mg_context *ctx, const char *path, struct vec *vec) |
| 5139 | { |
| 5140 |  struct vec ext_vec, mime_vec; |
| 5141 |  const char *list, *ext; |
| 5142 |  size_t path_len; |
| 5143 | |
| 5144 |  path_len = strlen(path); |
| 5145 | |
| 5146 |  if (ctx == NULL || vec == NULL) { |
| 5147 |    return; |
| 5148 |  } |
| 5149 | |
| 5150 |  /* Scan user-defined mime types first, in case user wants to |
| 5151 |   * override default mime types. */ |
| 5152 |  list = ctx->config[EXTRA_MIME_TYPES]; |
| 5153 |  while ((list = next_option(list, &ext_vec, &mime_vec)) != NULL) { |
| 5154 |    /* ext now points to the path suffix */ |
| 5155 |    ext = path + path_len - ext_vec.len; |
| 5156 |    if (mg_strncasecmp(ext, ext_vec.ptr, ext_vec.len) == 0) { |
| 5157 |      *vec = mime_vec; |
| 5158 |      return; |
| 5159 |    } |
| 5160 |  } |
| 5161 | |
| 5162 |  vec->ptr = mg_get_builtin_mime_type(path); |
| 5163 |  vec->len = strlen(vec->ptr); |
| 5164 | } |
| 5165 | |
| 5166 | |
| 5167 | /* Stringify binary data. Output buffer must be twice as big as input, |
| 5168 | * because each byte takes 2 bytes in string representation */ |
| 5169 | static void |
| 5170 | bin2str(char *to, const unsigned char *p, size_t len) |
| 5171 | { |
| 5172 |  static const char *hex = "0123456789abcdef"; |
| 5173 | |
| 5174 |  for (; len--; p++) { |
| 5175 |    *to++ = hex[p[0] >> 4]; |
| 5176 |    *to++ = hex[p[0] & 0x0f]; |
| 5177 |  } |
| 5178 |  *to = '\0'; |
| 5179 | } |
| 5180 | |
| 5181 | |
| 5182 | /* Return stringified MD5 hash for list of strings. Buffer must be 33 bytes. */ |
| 5183 | char * |
| 5184 | mg_md5(char buf[33], ...) |
| 5185 | { |
| 5186 |  md5_byte_t hash[16]; |
| 5187 |  const char *p; |
| 5188 |  va_list ap; |
| 5189 |  md5_state_t ctx; |
| 5190 | |
| 5191 |  md5_init(&ctx); |
| 5192 | |
| 5193 |  va_start(ap, buf); |
| 5194 |  while ((p = va_arg(ap, const char *)) != NULL) { |
| 5195 |    md5_append(&ctx, (const md5_byte_t *)p, strlen(p)); |
| 5196 |  } |
| 5197 |  va_end(ap); |
| 5198 | |
| 5199 |  md5_finish(&ctx, hash); |
| 5200 |  bin2str(buf, hash, sizeof(hash)); |
| 5201 |  return buf; |
| 5202 | } |
| 5203 | |
| 5204 | |
| 5205 | /* Check the user's password, return 1 if OK */ |
| 5206 | static int |
| 5207 | check_password(const char *method, |
| 5208 | const char *ha1, |
| 5209 | const char *uri, |
| 5210 | const char *nonce, |
| 5211 | const char *nc, |
| 5212 | const char *cnonce, |
| 5213 | const char *qop, |
| 5214 | const char *response) |
| 5215 | { |
| 5216 |  char ha2[32 + 1], expected_response[32 + 1]; |
| 5217 | |
| 5218 |  /* Some of the parameters may be NULL */ |
| 5219 |  if (method == NULL || nonce == NULL || nc == NULL || cnonce == NULL |
| 5220 |   || qop == NULL |
| 5221 |   || response == NULL) { |
| 5222 |    return 0; |
| 5223 |  } |
| 5224 | |
| 5225 |  /* NOTE(lsm): due to a bug in MSIE, we do not compare the URI */ |
| 5226 |  if (strlen(response) != 32) { |
| 5227 |    return 0; |
| 5228 |  } |
| 5229 | |
| 5230 |  mg_md5(ha2, method, ":", uri, NULL); |
| 5231 |  mg_md5(expected_response, |
| 5232 |   ha1, |
| 5233 |   ":", |
| 5234 |   nonce, |
| 5235 |   ":", |
| 5236 |   nc, |
| 5237 |   ":", |
| 5238 |   cnonce, |
| 5239 |   ":", |
| 5240 |   qop, |
| 5241 |   ":", |
| 5242 |   ha2, |
| 5243 |   NULL); |
| 5244 | |
| 5245 |  return mg_strcasecmp(response, expected_response) == 0; |
| 5246 | } |
| 5247 | |
| 5248 | |
| 5249 | /* Use the global passwords file, if specified by auth_gpass option, |
| 5250 | * or search for .htpasswd in the requested directory. */ |
| 5251 | static void |
| 5252 | open_auth_file(struct mg_connection *conn, const char *path, struct file *filep) |
| 5253 | { |
| 5254 |  if (conn != NULL && conn->ctx != NULL) { |
| 5255 |    char name[PATH_MAX]; |
| 5256 |    const char *p, *e, *gpass = conn->ctx->config[GLOBAL_PASSWORDS_FILE]; |
| 5257 |    struct file file = STRUCT_FILE_INITIALIZER; |
| 5258 |    int truncated; |
| 5259 | |
| 5260 |    if (gpass != NULL) { |
| 5261 |      /* Use global passwords file */ |
| 5262 |      if (!mg_fopen(conn, gpass, "r", filep)) { |
| 5263 | #ifdef DEBUG |
| 5264 |        mg_cry(conn, "fopen(%s): %s", gpass, strerror(ERRNO)); |
| 5265 | #endif |
| 5266 |      } |
| 5267 |      /* Important: using local struct file to test path for is_directory |
| 5268 |       * flag. If filep is used, mg_stat() makes it appear as if auth file |
| 5269 |       * was opened. */ |
| 5270 |    } else if (mg_stat(conn, path, &file) && file.is_directory) { |
| 5271 |      mg_snprintf(conn, |
| 5272 |       &truncated, |
| 5273 |       name, |
| 5274 |       sizeof(name), |
| 5275 |       "%s/%s", |
| 5276 |       path, |
| 5277 |       PASSWORDS_FILE_NAME); |
| 5278 | |
| 5279 |      if (truncated || !mg_fopen(conn, name, "r", filep)) { |
| 5280 | #ifdef DEBUG |
| 5281 |        mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); |
| 5282 | #endif |
| 5283 |      } |
| 5284 |    } else { |
| 5285 |      /* Try to find .htpasswd in requested directory. */ |
| 5286 |      for (p = path, e = p + strlen(p) - 1; e > p; e--) { |
| 5287 |        if (e[0] == '/') { |
| 5288 |          break; |
| 5289 |        } |
| 5290 |      } |
| 5291 |      mg_snprintf(conn, |
| 5292 |       &truncated, |
| 5293 |       name, |
| 5294 |       sizeof(name), |
| 5295 |       "%.*s/%s", |
| 5296 |       (int)(e - p), |
| 5297 |       p, |
| 5298 |       PASSWORDS_FILE_NAME); |
| 5299 | |
| 5300 |      if (truncated || !mg_fopen(conn, name, "r", filep)) { |
| 5301 | #ifdef DEBUG |
| 5302 |        mg_cry(conn, "fopen(%s): %s", name, strerror(ERRNO)); |
| 5303 | #endif |
| 5304 |      } |
| 5305 |    } |
| 5306 |  } |
| 5307 | } |
| 5308 | |
| 5309 | |
| 5310 | /* Parsed Authorization header */ |
| 5311 | struct ah { |
| 5312 |  char *user, *uri, *cnonce, *response, *qop, *nc, *nonce; |
| 5313 | }; |
| 5314 | |
| 5315 | |
| 5316 | /* Return 1 on success. Always initializes the ah structure. */ |
| 5317 | static int |
| 5318 | parse_auth_header(struct mg_connection *conn, |
| 5319 | char *buf, |
| 5320 | size_t buf_size, |
| 5321 | struct ah *ah) |
| 5322 | { |
| 5323 |  char *name, *value, *s; |
| 5324 |  const char *auth_header; |
| 5325 |  uint64_t nonce; |
| 5326 | |
| 5327 |  if (!ah || !conn) { |
| 5328 |    return 0; |
| 5329 |  } |
| 5330 | |
| 5331 |  (void)memset(ah, 0, sizeof(*ah)); |
| 5332 |  if ((auth_header = mg_get_header(conn, "Authorization")) == NULL |
| 5333 |   || mg_strncasecmp(auth_header, "Digest ", 7) != 0) { |
| 5334 |    return 0; |
| 5335 |  } |
| 5336 | |
| 5337 |  /* Make modifiable copy of the auth header */ |
| 5338 |  (void)mg_strlcpy(buf, auth_header + 7, buf_size); |
| 5339 |  s = buf; |
| 5340 | |
| 5341 |  /* Parse authorization header */ |
| 5342 |  for (;;) { |
| 5343 |    /* Gobble initial spaces */ |
| 5344 |    while (isspace(*(unsigned char *)s)) { |
| 5345 |      s++; |
| 5346 |    } |
| 5347 |    name = skip_quoted(&s, "=", " ", 0); |
| 5348 |    /* Value is either quote-delimited, or ends at first comma or space. */ |
| 5349 |    if (s[0] == '\"') { |
| 5350 |      s++; |
| 5351 |      value = skip_quoted(&s, "\"", " ", '\\'); |
| 5352 |      if (s[0] == ',') { |
| 5353 |        s++; |
| 5354 |      } |
| 5355 |    } else { |
| 5356 |      value = skip_quoted(&s, ", ", " ", 0); /* IE uses commas, FF uses |
| 5357 |       * spaces */ |
| 5358 |    } |
| 5359 |    if (*name == '\0') { |
| 5360 |      break; |
| 5361 |    } |
| 5362 | |
| 5363 |    if (!strcmp(name, "username")) { |
| 5364 |      ah->user = value; |
| 5365 |    } else if (!strcmp(name, "cnonce")) { |
| 5366 |      ah->cnonce = value; |
| 5367 |    } else if (!strcmp(name, "response")) { |
| 5368 |      ah->response = value; |
| 5369 |    } else if (!strcmp(name, "uri")) { |
| 5370 |      ah->uri = value; |
| 5371 |    } else if (!strcmp(name, "qop")) { |
| 5372 |      ah->qop = value; |
| 5373 |    } else if (!strcmp(name, "nc")) { |
| 5374 |      ah->nc = value; |
| 5375 |    } else if (!strcmp(name, "nonce")) { |
| 5376 |      ah->nonce = value; |
| 5377 |    } |
| 5378 |  } |
| 5379 | |
| 5380 | #ifndef NO_NONCE_CHECK |
| 5381 |  /* Read the nonce from the response. */ |
| 5382 |  if (ah->nonce == NULL) { |
| 5383 |    return 0; |
| 5384 |  } |
| 5385 |  s = NULL; |
| 5386 |  nonce = strtoull(ah->nonce, &s, 10); |
| 5387 |  if ((s == NULL) || (*s != 0)) { |
| 5388 |    return 0; |
| 5389 |  } |
| 5390 | |
| 5391 |  /* Convert the nonce from the client to a number. */ |
| 5392 |  nonce ^= conn->ctx->auth_nonce_mask; |
| 5393 | |
| 5394 |  /* The converted number corresponds to the time the nounce has been |
| 5395 |   * created. This should not be earlier than the server start. */ |
| 5396 |  /* Server side nonce check is valuable in all situations but one: |
| 5397 |   * if the server restarts frequently, but the client should not see |
| 5398 |   * that, so the server should accept nonces from previous starts. */ |
| 5399 |  /* However, the reasonable default is to not accept a nonce from a |
| 5400 |   * previous start, so if anyone changed the access rights between |
| 5401 |   * two restarts, a new login is required. */ |
| 5402 |  if (nonce < (uint64_t)conn->ctx->start_time) { |
| 5403 |    /* nonce is from a previous start of the server and no longer valid |
| 5404 |     * (replay attack?) */ |
| 5405 |    return 0; |
| 5406 |  } |
| 5407 |  /* Check if the nonce is too high, so it has not (yet) been used by the |
| 5408 |   * server. */ |
| 5409 |  if (nonce >= ((uint64_t)conn->ctx->start_time + conn->ctx->nonce_count)) { |
| 5410 |    return 0; |
| 5411 |  } |
| 5412 | #endif |
| 5413 | |
| 5414 |  /* CGI needs it as REMOTE_USER */ |
| 5415 |  if (ah->user != NULL) { |
| 5416 |    conn->request_info.remote_user = mg_strdup(ah->user); |
| 5417 |  } else { |
| 5418 |    return 0; |
| 5419 |  } |
| 5420 | |
| 5421 |  return 1; |
| 5422 | } |
| 5423 | |
| 5424 | |
| 5425 | static const char * |
| 5426 | mg_fgets(char *buf, size_t size, struct file *filep, char **p) |
| 5427 | { |
| 5428 |  const char *eof; |
| 5429 |  size_t len; |
| 5430 |  const char *memend; |
| 5431 | |
| 5432 |  if (!filep) { |
| 5433 |    return NULL; |
| 5434 |  } |
| 5435 | |
| 5436 |  if (filep->membuf != NULL && *p != NULL) { |
| 5437 |    memend = (const char *)&filep->membuf[filep->size]; |
| 5438 |    /* Search for \n from p till the end of stream */ |
| 5439 |    eof = (char *)memchr(*p, '\n', (size_t)(memend - *p)); |
| 5440 |    if (eof != NULL) { |
| 5441 |      eof += 1; /* Include \n */ |
| 5442 |    } else { |
| 5443 |      eof = memend; /* Copy remaining data */ |
| 5444 |    } |
| 5445 |    len = (size_t)(eof - *p) > size - 1 ? size - 1 : (size_t)(eof - *p); |
| 5446 |    memcpy(buf, *p, len); |
| 5447 |    buf[len] = '\0'; |
| 5448 |    *p += len; |
| 5449 |    return len ? eof : NULL; |
| 5450 |  } else if (filep->fp != NULL) { |
| 5451 |    return fgets(buf, (int)size, filep->fp); |
| 5452 |  } else { |
| 5453 |    return NULL; |
| 5454 |  } |
| 5455 | } |
| 5456 | |
| 5457 | struct read_auth_file_struct { |
| 5458 |  struct mg_connection *conn; |
| 5459 |  struct ah ah; |
| 5460 |  char *domain; |
| 5461 |  char buf[256 + 256 + 40]; |
| 5462 |  char *f_user; |
| 5463 |  char *f_domain; |
| 5464 |  char *f_ha1; |
| 5465 | }; |
| 5466 | |
| 5467 | |
| 5468 | static int |
| 5469 | read_auth_file(struct file *filep, struct read_auth_file_struct *workdata) |
| 5470 | { |
| 5471 |  char *p; |
| 5472 |  int is_authorized = 0; |
| 5473 |  struct file fp; |
| 5474 |  size_t l; |
| 5475 | |
| 5476 |  if (!filep || !workdata) { |
| 5477 |    return 0; |
| 5478 |  } |
| 5479 | |
| 5480 |  /* Loop over passwords file */ |
| 5481 |  p = (char *)filep->membuf; |
| 5482 |  while (mg_fgets(workdata->buf, sizeof(workdata->buf), filep, &p) != NULL) { |
| 5483 |    l = strlen(workdata->buf); |
| 5484 |    while (l > 0) { |
| 5485 |      if (isspace(workdata->buf[l - 1]) |
| 5486 |       || iscntrl(workdata->buf[l - 1])) { |
| 5487 |        l--; |
| 5488 |        workdata->buf[l] = 0; |
| 5489 |      } else |
| 5490 |        break; |
| 5491 |    } |
| 5492 |    if (l < 1) { |
| 5493 |      continue; |
| 5494 |    } |
| 5495 | |
| 5496 |    workdata->f_user = workdata->buf; |
| 5497 | |
| 5498 |    if (workdata->f_user[0] == ':') { |
| 5499 |      /* user names may not contain a ':' and may not be empty, |
| 5500 |       * so lines starting with ':' may be used for a special purpose */ |
| 5501 |      if (workdata->f_user[1] == '#') { |
| 5502 |        /* :# is a comment */ |
| 5503 |        continue; |
| 5504 |      } else if (!strncmp(workdata->f_user + 1, "include=", 8)) { |
| 5505 |        if (mg_fopen(workdata->conn, workdata->f_user + 9, "r", &fp)) { |
| 5506 |          is_authorized = read_auth_file(&fp, workdata); |
| 5507 |          mg_fclose(&fp); |
| 5508 |        } else { |
| 5509 |          mg_cry(workdata->conn, |
| 5510 |           "%s: cannot open authorization file: %s", |
| 5511 |           __func__, |
| 5512 |           workdata->buf); |
| 5513 |        } |
| 5514 |        continue; |
| 5515 |      } |
| 5516 |      /* everything is invalid for the moment (might change in the |
| 5517 |       * future) */ |
| 5518 |      mg_cry(workdata->conn, |
| 5519 |       "%s: syntax error in authorization file: %s", |
| 5520 |       __func__, |
| 5521 |       workdata->buf); |
| 5522 |      continue; |
| 5523 |    } |
| 5524 | |
| 5525 |    workdata->f_domain = strchr(workdata->f_user, ':'); |
| 5526 |    if (workdata->f_domain == NULL) { |
| 5527 |      mg_cry(workdata->conn, |
| 5528 |       "%s: syntax error in authorization file: %s", |
| 5529 |       __func__, |
| 5530 |       workdata->buf); |
| 5531 |      continue; |
| 5532 |    } |
| 5533 |    *(workdata->f_domain) = 0; |
| 5534 |    (workdata->f_domain)++; |
| 5535 | |
| 5536 |    workdata->f_ha1 = strchr(workdata->f_domain, ':'); |
| 5537 |    if (workdata->f_ha1 == NULL) { |
| 5538 |      mg_cry(workdata->conn, |
| 5539 |       "%s: syntax error in authorization file: %s", |
| 5540 |       __func__, |
| 5541 |       workdata->buf); |
| 5542 |      continue; |
| 5543 |    } |
| 5544 |    *(workdata->f_ha1) = 0; |
| 5545 |    (workdata->f_ha1)++; |
| 5546 | |
| 5547 |    if (!strcmp(workdata->ah.user, workdata->f_user) |
| 5548 |     && !strcmp(workdata->domain, workdata->f_domain)) { |
| 5549 |      return check_password(workdata->conn->request_info.request_method, |
| 5550 |       workdata->f_ha1, |
| 5551 |       workdata->ah.uri, |
| 5552 |       workdata->ah.nonce, |
| 5553 |       workdata->ah.nc, |
| 5554 |       workdata->ah.cnonce, |
| 5555 |       workdata->ah.qop, |
| 5556 |       workdata->ah.response); |
| 5557 |    } |
| 5558 |  } |
| 5559 | |
| 5560 |  return is_authorized; |
| 5561 | } |
| 5562 | |
| 5563 | |
| 5564 | /* Authorize against the opened passwords file. Return 1 if authorized. */ |
| 5565 | static int |
| 5566 | authorize(struct mg_connection *conn, struct file *filep) |
| 5567 | { |
| 5568 |  struct read_auth_file_struct workdata; |
| 5569 |  char buf[MG_BUF_LEN]; |
| 5570 | |
| 5571 |  if (!conn || !conn->ctx) { |
| 5572 |    return 0; |
| 5573 |  } |
| 5574 | |
| 5575 |  memset(&workdata, 0, sizeof(workdata)); |
| 5576 |  workdata.conn = conn; |
| 5577 | |
| 5578 |  if (!parse_auth_header(conn, buf, sizeof(buf), &workdata.ah)) { |
| 5579 |    return 0; |
| 5580 |  } |
| 5581 |  workdata.domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; |
| 5582 | |
| 5583 |  return read_auth_file(filep, &workdata); |
| 5584 | } |
| 5585 | |
| 5586 | |
| 5587 | /* Return 1 if request is authorised, 0 otherwise. */ |
| 5588 | static int |
| 5589 | check_authorization(struct mg_connection *conn, const char *path) |
| 5590 | { |
| 5591 |  char fname[PATH_MAX]; |
| 5592 |  struct vec uri_vec, filename_vec; |
| 5593 |  const char *list; |
| 5594 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 5595 |  int authorized = 1, truncated; |
| 5596 | |
| 5597 |  if (!conn || !conn->ctx) { |
| 5598 |    return 0; |
| 5599 |  } |
| 5600 | |
| 5601 |  list = conn->ctx->config[PROTECT_URI]; |
| 5602 |  while ((list = next_option(list, &uri_vec, &filename_vec)) != NULL) { |
| 5603 |    if (!memcmp(conn->request_info.local_uri, uri_vec.ptr, uri_vec.len)) { |
| 5604 |      mg_snprintf(conn, |
| 5605 |       &truncated, |
| 5606 |       fname, |
| 5607 |       sizeof(fname), |
| 5608 |       "%.*s", |
| 5609 |       (int)filename_vec.len, |
| 5610 |       filename_vec.ptr); |
| 5611 | |
| 5612 |      if (truncated || !mg_fopen(conn, fname, "r", &file)) { |
| 5613 |        mg_cry(conn, |
| 5614 |         "%s: cannot open %s: %s", |
| 5615 |         __func__, |
| 5616 |         fname, |
| 5617 |         strerror(errno)); |
| 5618 |      } |
| 5619 |      break; |
| 5620 |    } |
| 5621 |  } |
| 5622 | |
| 5623 |  if (!is_file_opened(&file)) { |
| 5624 |    open_auth_file(conn, path, &file); |
| 5625 |  } |
| 5626 | |
| 5627 |  if (is_file_opened(&file)) { |
| 5628 |    authorized = authorize(conn, &file); |
| 5629 |    mg_fclose(&file); |
| 5630 |  } |
| 5631 | |
| 5632 |  return authorized; |
| 5633 | } |
| 5634 | |
| 5635 | |
| 5636 | static void |
| 5637 | send_authorization_request(struct mg_connection *conn) |
| 5638 | { |
| 5639 |  char date[64]; |
| 5640 |  time_t curtime = time(NULL); |
| 5641 | |
| 5642 |  if (conn && conn->ctx) { |
| 5643 |    uint64_t nonce = (uint64_t)(conn->ctx->start_time); |
| 5644 | |
| 5645 |    (void)pthread_mutex_lock(&conn->ctx->nonce_mutex); |
| 5646 |    nonce += conn->ctx->nonce_count; |
| 5647 |    ++conn->ctx->nonce_count; |
| 5648 |    (void)pthread_mutex_unlock(&conn->ctx->nonce_mutex); |
| 5649 | |
| 5650 |    nonce ^= conn->ctx->auth_nonce_mask; |
| 5651 |    conn->status_code = 401; |
| 5652 |    conn->must_close = 1; |
| 5653 | |
| 5654 |    gmt_time_string(date, sizeof(date), &curtime); |
| 5655 | |
| 5656 |    mg_printf(conn, "HTTP/1.1 401 Unauthorized\r\n"); |
| 5657 |    send_no_cache_header(conn); |
| 5658 |    mg_printf(conn, |
| 5659 |     "Date: %s\r\n" |
| 5660 |     "Connection: %s\r\n" |
| 5661 |     "Content-Length: 0\r\n" |
| 5662 |     "WWW-Authenticate: Digest qop=\"auth\", realm=\"%s\", " |
| 5663 |     "nonce=\"%" UINT64_FMT "\"\r\n\r\n", |
| 5664 |     date, |
| 5665 |     suggest_connection_header(conn), |
| 5666 |     conn->ctx->config[AUTHENTICATION_DOMAIN], |
| 5667 |     nonce); |
| 5668 |  } |
| 5669 | } |
| 5670 | |
| 5671 | |
| 5672 | #if !defined(NO_FILES) |
| 5673 | static int |
| 5674 | is_authorized_for_put(struct mg_connection *conn) |
| 5675 | { |
| 5676 |  if (conn) { |
| 5677 |    struct file file = STRUCT_FILE_INITIALIZER; |
| 5678 |    const char *passfile = conn->ctx->config[PUT_DELETE_PASSWORDS_FILE]; |
| 5679 |    int ret = 0; |
| 5680 | |
| 5681 |    if (passfile != NULL && mg_fopen(conn, passfile, "r", &file)) { |
| 5682 |      ret = authorize(conn, &file); |
| 5683 |      mg_fclose(&file); |
| 5684 |    } |
| 5685 | |
| 5686 |    return ret; |
| 5687 |  } |
| 5688 |  return 0; |
| 5689 | } |
| 5690 | #endif |
| 5691 | |
| 5692 | |
| 5693 | int |
| 5694 | mg_modify_passwords_file(const char *fname, |
| 5695 | const char *domain, |
| 5696 | const char *user, |
| 5697 | const char *pass) |
| 5698 | { |
| 5699 |  int found, i; |
| 5700 |  char line[512], u[512] = "", d[512] = "", ha1[33], tmp[PATH_MAX + 8]; |
| 5701 |  FILE *fp, *fp2; |
| 5702 | |
| 5703 |  found = 0; |
| 5704 |  fp = fp2 = NULL; |
| 5705 | |
| 5706 |  /* Regard empty password as no password - remove user record. */ |
| 5707 |  if (pass != NULL && pass[0] == '\0') { |
| 5708 |    pass = NULL; |
| 5709 |  } |
| 5710 | |
| 5711 |  /* Other arguments must not be empty */ |
| 5712 |  if (fname == NULL || domain == NULL || user == NULL) { |
| 5713 |    return 0; |
| 5714 |  } |
| 5715 | |
| 5716 |  /* Using the given file format, user name and domain must not contain ':' |
| 5717 |   */ |
| 5718 |  if (strchr(user, ':') != NULL) { |
| 5719 |    return 0; |
| 5720 |  } |
| 5721 |  if (strchr(domain, ':') != NULL) { |
| 5722 |    return 0; |
| 5723 |  } |
| 5724 | |
| 5725 |  /* Do not allow control characters like newline in user name and domain. |
| 5726 |   * Do not allow excessively long names either. */ |
| 5727 |  for (i = 0; i < 255 && user[i] != 0; i++) { |
| 5728 |    if (iscntrl(user[i])) { |
| 5729 |      return 0; |
| 5730 |    } |
| 5731 |  } |
| 5732 |  if (user[i]) { |
| 5733 |    return 0; |
| 5734 |  } |
| 5735 |  for (i = 0; i < 255 && domain[i] != 0; i++) { |
| 5736 |    if (iscntrl(domain[i])) { |
| 5737 |      return 0; |
| 5738 |    } |
| 5739 |  } |
| 5740 |  if (domain[i]) { |
| 5741 |    return 0; |
| 5742 |  } |
| 5743 | |
| 5744 |  /* The maximum length of the path to the password file is limited */ |
| 5745 |  if ((strlen(fname) + 4) >= PATH_MAX) { |
| 5746 |    return 0; |
| 5747 |  } |
| 5748 | |
| 5749 |  /* Create a temporary file name. Length has been checked before. */ |
| 5750 |  strcpy(tmp, fname); |
| 5751 |  strcat(tmp, ".tmp"); |
| 5752 | |
| 5753 |  /* Create the file if does not exist */ |
| 5754 |  /* Use of fopen here is OK, since fname is only ASCII */ |
| 5755 |  if ((fp = fopen(fname, "a+")) != NULL) { |
| 5756 |    (void)fclose(fp); |
| 5757 |  } |
| 5758 | |
| 5759 |  /* Open the given file and temporary file */ |
| 5760 |  if ((fp = fopen(fname, "r")) == NULL) { |
| 5761 |    return 0; |
| 5762 |  } else if ((fp2 = fopen(tmp, "w+")) == NULL) { |
| 5763 |    fclose(fp); |
| 5764 |    return 0; |
| 5765 |  } |
| 5766 | |
| 5767 |  /* Copy the stuff to temporary file */ |
| 5768 |  while (fgets(line, sizeof(line), fp) != NULL) { |
| 5769 |    if (sscanf(line, "%255[^:]:%255[^:]:%*s", u, d) != 2) { |
| 5770 |      continue; |
| 5771 |    } |
| 5772 |    u[255] = 0; |
| 5773 |    d[255] = 0; |
| 5774 | |
| 5775 |    if (!strcmp(u, user) && !strcmp(d, domain)) { |
| 5776 |      found++; |
| 5777 |      if (pass != NULL) { |
| 5778 |        mg_md5(ha1, user, ":", domain, ":", pass, NULL); |
| 5779 |        fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); |
| 5780 |      } |
| 5781 |    } else { |
| 5782 |      fprintf(fp2, "%s", line); |
| 5783 |    } |
| 5784 |  } |
| 5785 | |
| 5786 |  /* If new user, just add it */ |
| 5787 |  if (!found && pass != NULL) { |
| 5788 |    mg_md5(ha1, user, ":", domain, ":", pass, NULL); |
| 5789 |    fprintf(fp2, "%s:%s:%s\n", user, domain, ha1); |
| 5790 |  } |
| 5791 | |
| 5792 |  /* Close files */ |
| 5793 |  fclose(fp); |
| 5794 |  fclose(fp2); |
| 5795 | |
| 5796 |  /* Put the temp file in place of real file */ |
| 5797 |  IGNORE_UNUSED_RESULT(remove(fname)); |
| 5798 |  IGNORE_UNUSED_RESULT(rename(tmp, fname)); |
| 5799 | |
| 5800 |  return 1; |
| 5801 | } |
| 5802 | |
| 5803 | |
| 5804 | static int |
| 5805 | is_valid_port(unsigned long port) |
| 5806 | { |
| 5807 |  return port < 0xffff; |
| 5808 | } |
| 5809 | |
| 5810 | |
| 5811 | static int |
| 5812 | mg_inet_pton(int af, const char *src, void *dst, size_t dstlen) |
| 5813 | { |
| 5814 |  struct addrinfo hints, *res, *ressave; |
| 5815 |  int func_ret = 0; |
| 5816 |  int gai_ret; |
| 5817 | |
| 5818 |  memset(&hints, 0, sizeof(struct addrinfo)); |
| 5819 |  hints.ai_family = af; |
| 5820 | |
| 5821 |  gai_ret = getaddrinfo(src, NULL, &hints, &res); |
| 5822 |  if (gai_ret != 0) { |
| 5823 |    /* gai_strerror could be used to convert gai_ret to a string */ |
| 5824 |    /* POSIX return values: see |
| 5825 |     * http://pubs.opengroup.org/onlinepubs/9699919799/functions/freeaddrinfo.html |
| 5826 |     */ |
| 5827 |    /* Windows return values: see |
| 5828 |     * https://msdn.microsoft.com/en-us/library/windows/desktop/ms738520%28v=vs.85%29.aspx |
| 5829 |     */ |
| 5830 |    return 0; |
| 5831 |  } |
| 5832 | |
| 5833 |  ressave = res; |
| 5834 | |
| 5835 |  while (res) { |
| 5836 |    if (dstlen >= res->ai_addrlen) { |
| 5837 |      memcpy(dst, res->ai_addr, res->ai_addrlen); |
| 5838 |      func_ret = 1; |
| 5839 |    } |
| 5840 |    res = res->ai_next; |
| 5841 |  } |
| 5842 | |
| 5843 |  freeaddrinfo(ressave); |
| 5844 |  return func_ret; |
| 5845 | } |
| 5846 | |
| 5847 | |
| 5848 | static int |
| 5849 | connect_socket(struct mg_context *ctx /* may be NULL */, |
| 5850 | const char *host, |
| 5851 | int port, |
| 5852 | int use_ssl, |
| 5853 | char *ebuf, |
| 5854 | size_t ebuf_len, |
| 5855 | SOCKET *sock /* output: socket, must not be NULL */, |
| 5856 | union usa *sa /* output: socket address, must not be NULL */ |
| 5857 | ) |
| 5858 | { |
| 5859 |  int ip_ver = 0; |
| 5860 |  *sock = INVALID_SOCKET; |
| 5861 |  memset(sa, 0, sizeof(*sa)); |
| 5862 | |
| 5863 |  if (ebuf_len > 0) { |
| 5864 |    *ebuf = 0; |
| 5865 |  } |
| 5866 | |
| 5867 |  if (host == NULL) { |
| 5868 |    mg_snprintf(NULL, |
| 5869 |     NULL, /* No truncation check for ebuf */ |
| 5870 |     ebuf, |
| 5871 |     ebuf_len, |
| 5872 |     "%s", |
| 5873 |     "NULL host"); |
| 5874 |    return 0; |
| 5875 |  } |
| 5876 | |
| 5877 |  if (port < 0 || !is_valid_port((unsigned)port)) { |
| 5878 |    mg_snprintf(NULL, |
| 5879 |     NULL, /* No truncation check for ebuf */ |
| 5880 |     ebuf, |
| 5881 |     ebuf_len, |
| 5882 |     "%s", |
| 5883 |     "invalid port"); |
| 5884 |    return 0; |
| 5885 |  } |
| 5886 | |
| 5887 |  if (use_ssl && (SSLv23_client_method == NULL)) { |
| 5888 |    mg_snprintf(NULL, |
| 5889 |     NULL, /* No truncation check for ebuf */ |
| 5890 |     ebuf, |
| 5891 |     ebuf_len, |
| 5892 |     "%s", |
| 5893 |     "SSL is not initialized"); |
| 5894 |    return 0; |
| 5895 |  } |
| 5896 | |
| 5897 |  if (mg_inet_pton(AF_INET, host, &sa->sin, sizeof(sa->sin))) { |
| 5898 |    sa->sin.sin_port = htons((uint16_t)port); |
| 5899 |    ip_ver = 4; |
| 5900 | #ifdef USE_IPV6 |
| 5901 |  } else if (mg_inet_pton(AF_INET6, host, &sa->sin6, sizeof(sa->sin6))) { |
| 5902 |    sa->sin6.sin6_port = htons((uint16_t)port); |
| 5903 |    ip_ver = 6; |
| 5904 |  } else if (host[0] == '[') { |
| 5905 |    /* While getaddrinfo on Windows will work with [::1], |
| 5906 |     * getaddrinfo on Linux only works with ::1 (without []). */ |
| 5907 |    size_t l = strlen(host + 1); |
| 5908 |    char *h = l > 1 ? mg_strdup(host + 1) : NULL; |
| 5909 |    if (h) { |
| 5910 |      h[l - 1] = 0; |
| 5911 |      if (mg_inet_pton(AF_INET6, h, &sa->sin6, sizeof(sa->sin6))) { |
| 5912 |        sa->sin6.sin6_port = htons((uint16_t)port); |
| 5913 |        ip_ver = 6; |
| 5914 |      } |
| 5915 |      mg_free(h); |
| 5916 |    } |
| 5917 | #endif |
| 5918 |  } |
| 5919 | |
| 5920 |  if (ip_ver == 0) { |
| 5921 |    mg_snprintf(NULL, |
| 5922 |     NULL, /* No truncation check for ebuf */ |
| 5923 |     ebuf, |
| 5924 |     ebuf_len, |
| 5925 |     "%s", |
| 5926 |     "host not found"); |
| 5927 |    return 0; |
| 5928 |  } |
| 5929 | |
| 5930 |  if (ip_ver == 4) { |
| 5931 |    *sock = socket(PF_INET, SOCK_STREAM, 0); |
| 5932 |  } |
| 5933 | #ifdef USE_IPV6 |
| 5934 |  else if (ip_ver == 6) { |
| 5935 |    *sock = socket(PF_INET6, SOCK_STREAM, 0); |
| 5936 |  } |
| 5937 | #endif |
| 5938 | |
| 5939 |  if (*sock == INVALID_SOCKET) { |
| 5940 |    mg_snprintf(NULL, |
| 5941 |     NULL, /* No truncation check for ebuf */ |
| 5942 |     ebuf, |
| 5943 |     ebuf_len, |
| 5944 |     "socket(): %s", |
| 5945 |     strerror(ERRNO)); |
| 5946 |    return 0; |
| 5947 |  } |
| 5948 | |
| 5949 |  set_close_on_exec(*sock, fc(ctx)); |
| 5950 | |
| 5951 |  if ((ip_ver == 4) |
| 5952 |   && (connect(*sock, (struct sockaddr *)&sa->sin, sizeof(sa->sin)) |
| 5953 |   == 0)) { |
| 5954 |    /* connected with IPv4 */ |
| 5955 |    return 1; |
| 5956 |  } |
| 5957 | |
| 5958 | #ifdef USE_IPV6 |
| 5959 |  if ((ip_ver == 6) |
| 5960 |   && (connect(*sock, (struct sockaddr *)&sa->sin6, sizeof(sa->sin6)) |
| 5961 |   == 0)) { |
| 5962 |    /* connected with IPv6 */ |
| 5963 |    return 1; |
| 5964 |  } |
| 5965 | #endif |
| 5966 | |
| 5967 |  /* Not connected */ |
| 5968 |  mg_snprintf(NULL, |
| 5969 |   NULL, /* No truncation check for ebuf */ |
| 5970 |   ebuf, |
| 5971 |   ebuf_len, |
| 5972 |   "connect(%s:%d): %s", |
| 5973 |   host, |
| 5974 |   port, |
| 5975 |   strerror(ERRNO)); |
| 5976 |  closesocket(*sock); |
| 5977 |  *sock = INVALID_SOCKET; |
| 5978 |  return 0; |
| 5979 | } |
| 5980 | |
| 5981 | |
| 5982 | int |
| 5983 | mg_url_encode(const char *src, char *dst, size_t dst_len) |
| 5984 | { |
| 5985 |  static const char *dont_escape = "._-$,;~()"; |
| 5986 |  static const char *hex = "0123456789abcdef"; |
| 5987 |  char *pos = dst; |
| 5988 |  const char *end = dst + dst_len - 1; |
| 5989 | |
| 5990 |  for (; *src != '\0' && pos < end; src++, pos++) { |
| 5991 |    if (isalnum(*(const unsigned char *)src) |
| 5992 |     || strchr(dont_escape, *(const unsigned char *)src) != NULL) { |
| 5993 |      *pos = *src; |
| 5994 |    } else if (pos + 2 < end) { |
| 5995 |      pos[0] = '%'; |
| 5996 |      pos[1] = hex[(*(const unsigned char *)src) >> 4]; |
| 5997 |      pos[2] = hex[(*(const unsigned char *)src) & 0xf]; |
| 5998 |      pos += 2; |
| 5999 |    } else { |
| 6000 |      break; |
| 6001 |    } |
| 6002 |  } |
| 6003 | |
| 6004 |  *pos = '\0'; |
| 6005 |  return (*src == '\0') ? (int)(pos - dst) : -1; |
| 6006 | } |
| 6007 | |
| 6008 | |
| 6009 | static void |
| 6010 | print_dir_entry(struct de *de) |
| 6011 | { |
| 6012 |  char size[64], mod[64], href[PATH_MAX]; |
| 6013 |  struct tm *tm; |
| 6014 | |
| 6015 |  if (de->file.is_directory) { |
| 6016 |    mg_snprintf(de->conn, |
| 6017 |     NULL, /* Buffer is big enough */ |
| 6018 |     size, |
| 6019 |     sizeof(size), |
| 6020 |     "%s", |
| 6021 |     "[DIRECTORY]"); |
| 6022 |  } else { |
| 6023 |    /* We use (signed) cast below because MSVC 6 compiler cannot |
| 6024 |     * convert unsigned __int64 to double. Sigh. */ |
| 6025 |    if (de->file.size < 1024) { |
| 6026 |      mg_snprintf(de->conn, |
| 6027 |       NULL, /* Buffer is big enough */ |
| 6028 |       size, |
| 6029 |       sizeof(size), |
| 6030 |       "%d", |
| 6031 |       (int)de->file.size); |
| 6032 |    } else if (de->file.size < 0x100000) { |
| 6033 |      mg_snprintf(de->conn, |
| 6034 |       NULL, /* Buffer is big enough */ |
| 6035 |       size, |
| 6036 |       sizeof(size), |
| 6037 |       "%.1fk", |
| 6038 |       (double)de->file.size / 1024.0); |
| 6039 |    } else if (de->file.size < 0x40000000) { |
| 6040 |      mg_snprintf(de->conn, |
| 6041 |       NULL, /* Buffer is big enough */ |
| 6042 |       size, |
| 6043 |       sizeof(size), |
| 6044 |       "%.1fM", |
| 6045 |       (double)de->file.size / 1048576); |
| 6046 |    } else { |
| 6047 |      mg_snprintf(de->conn, |
| 6048 |       NULL, /* Buffer is big enough */ |
| 6049 |       size, |
| 6050 |       sizeof(size), |
| 6051 |       "%.1fG", |
| 6052 |       (double)de->file.size / 1073741824); |
| 6053 |    } |
| 6054 |  } |
| 6055 | |
| 6056 |  /* Note: mg_snprintf will not cause a buffer overflow above. |
| 6057 |   * So, string truncation checks are not required here. */ |
| 6058 | |
| 6059 |  tm = localtime(&de->file.last_modified); |
| 6060 |  if (tm != NULL) { |
| 6061 |    strftime(mod, sizeof(mod), "%d-%b-%Y %H:%M", tm); |
| 6062 |  } else { |
| 6063 |    mg_strlcpy(mod, "01-Jan-1970 00:00", sizeof(mod)); |
| 6064 |    mod[sizeof(mod) - 1] = '\0'; |
| 6065 |  } |
| 6066 |  mg_url_encode(de->file_name, href, sizeof(href)); |
| 6067 |  de->conn->num_bytes_sent += |
| 6068 |   mg_printf(de->conn, |
| 6069 |   "<tr><td><a href=\"%s%s%s\">%s%s</a></td>" |
| 6070 |   "<td> %s</td><td> %s</td></tr>\n", |
| 6071 |   de->conn->request_info.local_uri, |
| 6072 |   href, |
| 6073 |   de->file.is_directory ? "/" : "", |
| 6074 |   de->file_name, |
| 6075 |   de->file.is_directory ? "/" : "", |
| 6076 |   mod, |
| 6077 |   size); |
| 6078 | } |
| 6079 | |
| 6080 | |
| 6081 | /* This function is called from send_directory() and used for |
| 6082 | * sorting directory entries by size, or name, or modification time. |
| 6083 | * On windows, __cdecl specification is needed in case if project is built |
| 6084 | * with __stdcall convention. qsort always requires __cdels callback. */ |
| 6085 | static int WINCDECL |
| 6086 | compare_dir_entries(const void *p1, const void *p2) |
| 6087 | { |
| 6088 |  if (p1 && p2) { |
| 6089 |    const struct de *a = (const struct de *)p1, *b = (const struct de *)p2; |
| 6090 |    const char *query_string = a->conn->request_info.query_string; |
| 6091 |    int cmp_result = 0; |
| 6092 | |
| 6093 |    if (query_string == NULL) { |
| 6094 |      query_string = "na"; |
| 6095 |    } |
| 6096 | |
| 6097 |    if (a->file.is_directory && !b->file.is_directory) { |
| 6098 |      return -1; /* Always put directories on top */ |
| 6099 |    } else if (!a->file.is_directory && b->file.is_directory) { |
| 6100 |      return 1; /* Always put directories on top */ |
| 6101 |    } else if (*query_string == 'n') { |
| 6102 |      cmp_result = strcmp(a->file_name, b->file_name); |
| 6103 |    } else if (*query_string == 's') { |
| 6104 |      cmp_result = a->file.size == b->file.size |
| 6105 |       ? 0 |
| 6106 |       : a->file.size > b->file.size ? 1 : -1; |
| 6107 |    } else if (*query_string == 'd') { |
| 6108 |      cmp_result = |
| 6109 |       (a->file.last_modified == b->file.last_modified) |
| 6110 |       ? 0 |
| 6111 |       : ((a->file.last_modified > b->file.last_modified) ? 1 |
| 6112 |       : -1); |
| 6113 |    } |
| 6114 | |
| 6115 |    return query_string[1] == 'd' ? -cmp_result : cmp_result; |
| 6116 |  } |
| 6117 |  return 0; |
| 6118 | } |
| 6119 | |
| 6120 | |
| 6121 | static int |
| 6122 | must_hide_file(struct mg_connection *conn, const char *path) |
| 6123 | { |
| 6124 |  if (conn && conn->ctx) { |
| 6125 |    const char *pw_pattern = "**" PASSWORDS_FILE_NAME "$"; |
| 6126 |    const char *pattern = conn->ctx->config[HIDE_FILES]; |
| 6127 |    return match_prefix(pw_pattern, strlen(pw_pattern), path) > 0 |
| 6128 |     || (pattern != NULL |
| 6129 |     && match_prefix(pattern, strlen(pattern), path) > 0); |
| 6130 |  } |
| 6131 |  return 0; |
| 6132 | } |
| 6133 | |
| 6134 | |
| 6135 | static int |
| 6136 | scan_directory(struct mg_connection *conn, |
| 6137 | const char *dir, |
| 6138 | void *data, |
| 6139 | void (*cb)(struct de *, void *)) |
| 6140 | { |
| 6141 |  char path[PATH_MAX]; |
| 6142 |  struct dirent *dp; |
| 6143 |  DIR *dirp; |
| 6144 |  struct de de; |
| 6145 |  int truncated; |
| 6146 | |
| 6147 |  if ((dirp = mg_opendir(conn, dir)) == NULL) { |
| 6148 |    return 0; |
| 6149 |  } else { |
| 6150 |    de.conn = conn; |
| 6151 | |
| 6152 |    while ((dp = mg_readdir(dirp)) != NULL) { |
| 6153 |      /* Do not show current dir and hidden files */ |
| 6154 |      if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..") |
| 6155 |       || must_hide_file(conn, dp->d_name)) { |
| 6156 |        continue; |
| 6157 |      } |
| 6158 | |
| 6159 |      mg_snprintf( |
| 6160 |       conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); |
| 6161 | |
| 6162 |      /* If we don't memset stat structure to zero, mtime will have |
| 6163 |       * garbage and strftime() will segfault later on in |
| 6164 |       * print_dir_entry(). memset is required only if mg_stat() |
| 6165 |       * fails. For more details, see |
| 6166 |       * http://code.google.com/p/mongoose/issues/detail?id=79 */ |
| 6167 |      memset(&de.file, 0, sizeof(de.file)); |
| 6168 | |
| 6169 |      if (truncated) { |
| 6170 |        /* If the path is not complete, skip processing. */ |
| 6171 |        continue; |
| 6172 |      } |
| 6173 | |
| 6174 |      if (!mg_stat(conn, path, &de.file)) { |
| 6175 |        mg_cry(conn, |
| 6176 |         "%s: mg_stat(%s) failed: %s", |
| 6177 |         __func__, |
| 6178 |         path, |
| 6179 |         strerror(ERRNO)); |
| 6180 |      } |
| 6181 |      de.file_name = dp->d_name; |
| 6182 |      cb(&de, data); |
| 6183 |    } |
| 6184 |    (void)mg_closedir(dirp); |
| 6185 |  } |
| 6186 |  return 1; |
| 6187 | } |
| 6188 | |
| 6189 | |
| 6190 | #if !defined(NO_FILES) |
| 6191 | static int |
| 6192 | remove_directory(struct mg_connection *conn, const char *dir) |
| 6193 | { |
| 6194 |  char path[PATH_MAX]; |
| 6195 |  struct dirent *dp; |
| 6196 |  DIR *dirp; |
| 6197 |  struct de de; |
| 6198 |  int truncated; |
| 6199 |  int ok = 1; |
| 6200 | |
| 6201 |  if ((dirp = mg_opendir(conn, dir)) == NULL) { |
| 6202 |    return 0; |
| 6203 |  } else { |
| 6204 |    de.conn = conn; |
| 6205 | |
| 6206 |    while ((dp = mg_readdir(dirp)) != NULL) { |
| 6207 |      /* Do not show current dir (but show hidden files as they will |
| 6208 |       * also be removed) */ |
| 6209 |      if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { |
| 6210 |        continue; |
| 6211 |      } |
| 6212 | |
| 6213 |      mg_snprintf( |
| 6214 |       conn, &truncated, path, sizeof(path), "%s/%s", dir, dp->d_name); |
| 6215 | |
| 6216 |      /* If we don't memset stat structure to zero, mtime will have |
| 6217 |       * garbage and strftime() will segfault later on in |
| 6218 |       * print_dir_entry(). memset is required only if mg_stat() |
| 6219 |       * fails. For more details, see |
| 6220 |       * http://code.google.com/p/mongoose/issues/detail?id=79 */ |
| 6221 |      memset(&de.file, 0, sizeof(de.file)); |
| 6222 | |
| 6223 |      if (truncated) { |
| 6224 |        /* Do not delete anything shorter */ |
| 6225 |        ok = 0; |
| 6226 |        continue; |
| 6227 |      } |
| 6228 | |
| 6229 |      if (!mg_stat(conn, path, &de.file)) { |
| 6230 |        mg_cry(conn, |
| 6231 |         "%s: mg_stat(%s) failed: %s", |
| 6232 |         __func__, |
| 6233 |         path, |
| 6234 |         strerror(ERRNO)); |
| 6235 |        ok = 0; |
| 6236 |      } |
| 6237 |      if (de.file.membuf == NULL) { |
| 6238 |        /* file is not in memory */ |
| 6239 |        if (de.file.is_directory) { |
| 6240 |          if (remove_directory(conn, path) == 0) { |
| 6241 |            ok = 0; |
| 6242 |          } |
| 6243 |        } else { |
| 6244 |          if (mg_remove(conn, path) == 0) { |
| 6245 |            ok = 0; |
| 6246 |          } |
| 6247 |        } |
| 6248 |      } else { |
| 6249 |        /* file is in memory. It can not be deleted. */ |
| 6250 |        ok = 0; |
| 6251 |      } |
| 6252 |    } |
| 6253 |    (void)mg_closedir(dirp); |
| 6254 | |
| 6255 |    IGNORE_UNUSED_RESULT(rmdir(dir)); |
| 6256 |  } |
| 6257 | |
| 6258 |  return ok; |
| 6259 | } |
| 6260 | #endif |
| 6261 | |
| 6262 | |
| 6263 | struct dir_scan_data { |
| 6264 |  struct de *entries; |
| 6265 |  unsigned int num_entries; |
| 6266 |  unsigned int arr_size; |
| 6267 | }; |
| 6268 | |
| 6269 | |
| 6270 | /* Behaves like realloc(), but frees original pointer on failure */ |
| 6271 | static void * |
| 6272 | realloc2(void *ptr, size_t size) |
| 6273 | { |
| 6274 |  void *new_ptr = mg_realloc(ptr, size); |
| 6275 |  if (new_ptr == NULL) { |
| 6276 |    mg_free(ptr); |
| 6277 |  } |
| 6278 |  return new_ptr; |
| 6279 | } |
| 6280 | |
| 6281 | |
| 6282 | static void |
| 6283 | dir_scan_callback(struct de *de, void *data) |
| 6284 | { |
| 6285 |  struct dir_scan_data *dsd = (struct dir_scan_data *)data; |
| 6286 | |
| 6287 |  if (dsd->entries == NULL || dsd->num_entries >= dsd->arr_size) { |
| 6288 |    dsd->arr_size *= 2; |
| 6289 |    dsd->entries = |
| 6290 |     (struct de *)realloc2(dsd->entries, |
| 6291 |     dsd->arr_size * sizeof(dsd->entries[0])); |
| 6292 |  } |
| 6293 |  if (dsd->entries == NULL) { |
| 6294 |    /* TODO(lsm, low): propagate an error to the caller */ |
| 6295 |    dsd->num_entries = 0; |
| 6296 |  } else { |
| 6297 |    dsd->entries[dsd->num_entries].file_name = mg_strdup(de->file_name); |
| 6298 |    dsd->entries[dsd->num_entries].file = de->file; |
| 6299 |    dsd->entries[dsd->num_entries].conn = de->conn; |
| 6300 |    dsd->num_entries++; |
| 6301 |  } |
| 6302 | } |
| 6303 | |
| 6304 | |
| 6305 | static void |
| 6306 | handle_directory_request(struct mg_connection *conn, const char *dir) |
| 6307 | { |
| 6308 |  unsigned int i; |
| 6309 |  int sort_direction; |
| 6310 |  struct dir_scan_data data = {NULL, 0, 128}; |
| 6311 |  char date[64]; |
| 6312 |  time_t curtime = time(NULL); |
| 6313 | |
| 6314 |  if (!scan_directory(conn, dir, &data, dir_scan_callback)) { |
| 6315 |    send_http_error(conn, |
| 6316 |     500, |
| 6317 |     "Error: Cannot open directory\nopendir(%s): %s", |
| 6318 |     dir, |
| 6319 |     strerror(ERRNO)); |
| 6320 |    return; |
| 6321 |  } |
| 6322 | |
| 6323 |  gmt_time_string(date, sizeof(date), &curtime); |
| 6324 | |
| 6325 |  if (!conn) { |
| 6326 |    return; |
| 6327 |  } |
| 6328 | |
| 6329 |  sort_direction = conn->request_info.query_string != NULL |
| 6330 |   && conn->request_info.query_string[1] == 'd' |
| 6331 |   ? 'a' |
| 6332 |   : 'd'; |
| 6333 | |
| 6334 |  conn->must_close = 1; |
| 6335 |  mg_printf(conn, "HTTP/1.1 200 OK\r\n"); |
| 6336 |  send_static_cache_header(conn); |
| 6337 |  mg_printf(conn, |
| 6338 |   "Date: %s\r\n" |
| 6339 |   "Connection: close\r\n" |
| 6340 |   "Content-Type: text/html; charset=utf-8\r\n\r\n", |
| 6341 |   date); |
| 6342 | |
| 6343 |  conn->num_bytes_sent += |
| 6344 |   mg_printf(conn, |
| 6345 |   "<html><head><title>Index of %s</title>" |
| 6346 |   "<style>th {text-align: left;}</style></head>" |
| 6347 |   "<body><h1>Index of %s</h1><pre><table cellpadding=\"0\">" |
| 6348 |   "<tr><th><a href=\"?n%c\">Name</a></th>" |
| 6349 |   "<th><a href=\"?d%c\">Modified</a></th>" |
| 6350 |   "<th><a href=\"?s%c\">Size</a></th></tr>" |
| 6351 |   "<tr><td colspan=\"3\"><hr></td></tr>", |
| 6352 |   conn->request_info.local_uri, |
| 6353 |   conn->request_info.local_uri, |
| 6354 |   sort_direction, |
| 6355 |   sort_direction, |
| 6356 |   sort_direction); |
| 6357 | |
| 6358 |  /* Print first entry - link to a parent directory */ |
| 6359 |  conn->num_bytes_sent += |
| 6360 |   mg_printf(conn, |
| 6361 |   "<tr><td><a href=\"%s%s\">%s</a></td>" |
| 6362 |   "<td> %s</td><td> %s</td></tr>\n", |
| 6363 |   conn->request_info.local_uri, |
| 6364 |   "..", |
| 6365 |   "Parent directory", |
| 6366 |   "-", |
| 6367 |   "-"); |
| 6368 | |
| 6369 |  /* Sort and print directory entries */ |
| 6370 |  if (data.entries != NULL) { |
| 6371 |    qsort(data.entries, |
| 6372 |     (size_t)data.num_entries, |
| 6373 |     sizeof(data.entries[0]), |
| 6374 |     compare_dir_entries); |
| 6375 |    for (i = 0; i < data.num_entries; i++) { |
| 6376 |      print_dir_entry(&data.entries[i]); |
| 6377 |      mg_free(data.entries[i].file_name); |
| 6378 |    } |
| 6379 |    mg_free(data.entries); |
| 6380 |  } |
| 6381 | |
| 6382 |  conn->num_bytes_sent += mg_printf(conn, "%s", "</table></body></html>"); |
| 6383 |  conn->status_code = 200; |
| 6384 | } |
| 6385 | |
| 6386 | |
| 6387 | /* Send len bytes from the opened file to the client. */ |
| 6388 | static void |
| 6389 | send_file_data(struct mg_connection *conn, |
| 6390 | struct file *filep, |
| 6391 | int64_t offset, |
| 6392 | int64_t len) |
| 6393 | { |
| 6394 |  char buf[MG_BUF_LEN]; |
| 6395 |  int to_read, num_read, num_written; |
| 6396 |  int64_t size; |
| 6397 | |
| 6398 |  if (!filep || !conn) { |
| 6399 |    return; |
| 6400 |  } |
| 6401 | |
| 6402 |  /* Sanity check the offset */ |
| 6403 |  size = filep->size > INT64_MAX ? INT64_MAX : (int64_t)(filep->size); |
| 6404 |  offset = offset < 0 ? 0 : offset > size ? size : offset; |
| 6405 | |
| 6406 |  if ( ) { |
| 6407 |    /* file stored in memory */ |
| 6408 |    if (len > size - offset) { |
| 6409 |      len = size - offset; |
| 6410 |    } |
| 6411 |    mg_write(conn, filep->membuf + offset, (size_t)len); |
| 6412 |  } else if (len > 0 && filep->fp != NULL) { |
| 6413 | /* file stored on disk */ |
| 6414 | #if defined(__linux__) |
| 6415 |    /* sendfile is only available for Linux */ |
| 6416 |    if (conn->throttle == 0 && conn->ssl == 0) { |
| 6417 |      off_t sf_offs = (off_t)offset; |
| 6418 |      ssize_t sf_sent; |
| 6419 |      int sf_file = fileno(filep->fp); |
| 6420 |      int loop_cnt = 0; |
| 6421 | |
| 6422 |      do { |
| 6423 |        /* 2147479552 (0x7FFFF000) is a limit found by experiment on |
| 6424 |         * 64 bit Linux (2^31 minus one memory page of 4k?). */ |
| 6425 |        size_t sf_tosend = |
| 6426 |         (size_t)((len < 0x7FFFF000) ? len : 0x7FFFF000); |
| 6427 |        sf_sent = |
| 6428 |         sendfile(conn->client.sock, sf_file, &sf_offs, sf_tosend); |
| 6429 |        if (sf_sent > 0) { |
| 6430 |          conn->num_bytes_sent += sf_sent; |
| 6431 |          len -= sf_sent; |
| 6432 |          offset += sf_sent; |
| 6433 |        } else if (loop_cnt == 0) { |
| 6434 |          /* This file can not be sent using sendfile. |
| 6435 |           * This might be the case for pseudo-files in the |
| 6436 |           * /sys/ and /proc/ file system. |
| 6437 |           * Use the regular user mode copy code instead. */ |
| 6438 |          break; |
| 6439 |        } else if (sf_sent == 0) { |
| 6440 |          /* No error, but 0 bytes sent. May be EOF? */ |
| 6441 |          return; |
| 6442 |        } |
| 6443 |        loop_cnt++; |
| 6444 | |
| 6445 |      } while ((len > 0) && (sf_sent >= 0)); |
| 6446 | |
| 6447 |      if (sf_sent > 0) { |
| 6448 |        return; /* OK */ |
| 6449 |      } |
| 6450 | |
| 6451 |      /* sf_sent<0 means error, thus fall back to the classic way */ |
| 6452 |      /* This is always the case, if sf_file is not a "normal" file, |
| 6453 |       * e.g., for sending data from the output of a CGI process. */ |
| 6454 |      offset = (int64_t)sf_offs; |
| 6455 |    } |
| 6456 | #endif |
| 6457 |    if ((offset > 0) && (fseeko(filep->fp, offset, SEEK_SET) != 0)) { |
| 6458 |      mg_cry(conn, "%s: fseeko() failed: %s", __func__, strerror(ERRNO)); |
| 6459 |      send_http_error( |
| 6460 |       conn, |
| 6461 |       500, |
| 6462 |       "%s", |
| 6463 |       "Error: Unable to access file at requested position."); |
| 6464 |    } else { |
| 6465 |      while (len > 0) { |
| 6466 |        /* Calculate how much to read from the file in the buffer */ |
| 6467 |        to_read = sizeof(buf); |
| 6468 |        if ((int64_t)to_read > len) { |
| 6469 |          to_read = (int)len; |
| 6470 |        } |
| 6471 | |
| 6472 |        /* Read from file, exit the loop on error */ |
| 6473 |        if ((num_read = (int)fread(buf, 1, (size_t)to_read, filep->fp)) |
| 6474 |         <= 0) { |
| 6475 |          break; |
| 6476 |        } |
| 6477 | |
| 6478 |        /* Send read bytes to the client, exit the loop on error */ |
| 6479 |        if ((num_written = mg_write(conn, buf, (size_t)num_read)) |
| 6480 |         != num_read) { |
| 6481 |          break; |
| 6482 |        } |
| 6483 | |
| 6484 |        /* Both read and were successful, adjust counters */ |
| 6485 |        conn->num_bytes_sent += num_written; |
| 6486 |        len -= num_written; |
| 6487 |      } |
| 6488 |    } |
| 6489 |  } |
| 6490 | } |
| 6491 | |
| 6492 | |
| 6493 | static int |
| 6494 | parse_range_header(const char *header, int64_t *a, int64_t *b) |
| 6495 | { |
| 6496 |  return sscanf(header, "bytes=%" INT64_FMT "-%" INT64_FMT, a, b); |
| 6497 | } |
| 6498 | |
| 6499 | |
| 6500 | static void |
| 6501 | construct_etag(char *buf, size_t buf_len, const struct file *filep) |
| 6502 | { |
| 6503 |  if (filep != NULL && buf != NULL) { |
| 6504 |    mg_snprintf(NULL, |
| 6505 |     NULL, /* All calls to construct_etag use 64 byte buffer */ |
| 6506 |     buf, |
| 6507 |     buf_len, |
| 6508 |     "\"%lx.%" INT64_FMT "\"", |
| 6509 |     (unsigned long)filep->last_modified, |
| 6510 |     filep->size); |
| 6511 |  } |
| 6512 | } |
| 6513 | |
| 6514 | |
| 6515 | static void |
| 6516 | fclose_on_exec(struct file *filep, struct mg_connection *conn) |
| 6517 | { |
| 6518 |  if (filep != NULL && filep->fp != NULL) { |
| 6519 | #ifdef _WIN32 |
| 6520 |    (void)conn; /* Unused. */ |
| 6521 | #else |
| 6522 |    if (fcntl(fileno(filep->fp), F_SETFD, FD_CLOEXEC) != 0) { |
| 6523 |      mg_cry(conn, |
| 6524 |       "%s: fcntl(F_SETFD FD_CLOEXEC) failed: %s", |
| 6525 |       __func__, |
| 6526 |       strerror(ERRNO)); |
| 6527 |    } |
| 6528 | #endif |
| 6529 |  } |
| 6530 | } |
| 6531 | |
| 6532 | |
| 6533 | static void |
| 6534 | handle_static_file_request(struct mg_connection *conn, |
| 6535 | const char *path, |
| 6536 | struct file *filep, |
| 6537 | const char *mime_type) |
| 6538 | { |
| 6539 |  char date[64], lm[64], etag[64]; |
| 6540 |  char range[128]; /* large enough, so there will be no overflow */ |
| 6541 |  const char *msg = "OK", *hdr; |
| 6542 |  time_t curtime = time(NULL); |
| 6543 |  int64_t cl, r1, r2; |
| 6544 |  struct vec mime_vec; |
| 6545 |  int n, truncated; |
| 6546 |  char gz_path[PATH_MAX]; |
| 6547 |  const char *encoding = ""; |
| 6548 |  const char *cors1, *cors2, *cors3; |
| 6549 | |
| 6550 |  if (conn == NULL || conn->ctx == NULL || filep == NULL) { |
| 6551 |    return; |
| 6552 |  } |
| 6553 | |
| 6554 |  if (mime_type == NULL) { |
| 6555 |    get_mime_type(conn->ctx, path, &mime_vec); |
| 6556 |  } else { |
| 6557 |    mime_vec.ptr = mime_type; |
| 6558 |    mime_vec.len = strlen(mime_type); |
| 6559 |  } |
| 6560 |  if (filep->size > INT64_MAX) { |
| 6561 |    send_http_error(conn, |
| 6562 |     500, |
| 6563 |     "Error: File size is too large to send\n%" INT64_FMT, |
| 6564 |     filep->size); |
| 6565 |  } |
| 6566 |  cl = (int64_t)filep->size; |
| 6567 |  conn->status_code = 200; |
| 6568 |  range[0] = '\0'; |
| 6569 | |
| 6570 |  /* if this file is in fact a pre-gzipped file, rewrite its filename |
| 6571 |   * it's important to rewrite the filename after resolving |
| 6572 |   * the mime type from it, to preserve the actual file's type */ |
| 6573 |  if (filep->gzipped) { |
| 6574 |    mg_snprintf(conn, &truncated, gz_path, sizeof(gz_path), "%s.gz", path); |
| 6575 | |
| 6576 |    if (truncated) { |
| 6577 |      send_http_error(conn, |
| 6578 |       500, |
| 6579 |       "Error: Path of zipped file too long (%s)", |
| 6580 |       path); |
| 6581 |      return; |
| 6582 |    } |
| 6583 | |
| 6584 |    path = gz_path; |
| 6585 |    encoding = "Content-Encoding: gzip\r\n"; |
| 6586 |  } |
| 6587 | |
| 6588 |  if (!mg_fopen(conn, path, "rb", filep)) { |
| 6589 |    send_http_error(conn, |
| 6590 |     500, |
| 6591 |     "Error: Cannot open file\nfopen(%s): %s", |
| 6592 |     path, |
| 6593 |     strerror(ERRNO)); |
| 6594 |    return; |
| 6595 |  } |
| 6596 | |
| 6597 |  fclose_on_exec(filep, conn); |
| 6598 | |
| 6599 |  /* If Range: header specified, act accordingly */ |
| 6600 |  r1 = r2 = 0; |
| 6601 |  hdr = mg_get_header(conn, "Range"); |
| 6602 |  if (hdr != NULL && (n = parse_range_header(hdr, &r1, &r2)) > 0 && r1 >= 0 |
| 6603 |   && r2 >= 0) { |
| 6604 |    /* actually, range requests don't play well with a pre-gzipped |
| 6605 |     * file (since the range is specified in the uncompressed space) */ |
| 6606 |    if (filep->gzipped) { |
| 6607 |      send_http_error( |
| 6608 |       conn, |
| 6609 |       501, |
| 6610 |       "%s", |
| 6611 |       "Error: Range requests in gzipped files are not supported"); |
| 6612 |      mg_fclose(filep); |
| 6613 |      return; |
| 6614 |    } |
| 6615 |    conn->status_code = 206; |
| 6616 |    cl = n == 2 ? (r2 > cl ? cl : r2) - r1 + 1 : cl - r1; |
| 6617 |    mg_snprintf(conn, |
| 6618 |     NULL, /* range buffer is big enough */ |
| 6619 |     range, |
| 6620 |     sizeof(range), |
| 6621 |     "Content-Range: bytes " |
| 6622 |     "%" INT64_FMT "-%" INT64_FMT "/%" INT64_FMT "\r\n", |
| 6623 |     r1, |
| 6624 |     r1 + cl - 1, |
| 6625 |     filep->size); |
| 6626 |    msg = "Partial Content"; |
| 6627 |  } |
| 6628 | |
| 6629 |  hdr = mg_get_header(conn, "Origin"); |
| 6630 |  if (hdr) { |
| 6631 |    /* Cross-origin resource sharing (CORS), see |
| 6632 |     * http://www.html5rocks.com/en/tutorials/cors/, |
| 6633 |     * http://www.html5rocks.com/static/images/cors_server_flowchart.png - |
| 6634 |     * preflight is not supported for files. */ |
| 6635 |    cors1 = "Access-Control-Allow-Origin: "; |
| 6636 |    cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; |
| 6637 |    cors3 = "\r\n"; |
| 6638 |  } else { |
| 6639 |    cors1 = cors2 = cors3 = ""; |
| 6640 |  } |
| 6641 | |
| 6642 |  /* Prepare Etag, Date, Last-Modified headers. Must be in UTC, according to |
| 6643 |   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.3 */ |
| 6644 |  gmt_time_string(date, sizeof(date), &curtime); |
| 6645 |  gmt_time_string(lm, sizeof(lm), &filep->last_modified); |
| 6646 |  construct_etag(etag, sizeof(etag), filep); |
| 6647 | |
| 6648 |  (void)mg_printf(conn, |
| 6649 |   "HTTP/1.1 %d %s\r\n" |
| 6650 |   "%s%s%s" |
| 6651 |   "Date: %s\r\n", |
| 6652 |   conn->status_code, |
| 6653 |   msg, |
| 6654 |   cors1, |
| 6655 |   cors2, |
| 6656 |   cors3, |
| 6657 |   date); |
| 6658 |  send_static_cache_header(conn); |
| 6659 |  (void)mg_printf(conn, |
| 6660 |   "Last-Modified: %s\r\n" |
| 6661 |   "Etag: %s\r\n" |
| 6662 |   "Content-Type: %.*s\r\n" |
| 6663 |   "Content-Length: %" INT64_FMT "\r\n" |
| 6664 |   "Connection: %s\r\n" |
| 6665 |   "Accept-Ranges: bytes\r\n" |
| 6666 |   "%s%s\r\n", |
| 6667 |   lm, |
| 6668 |   etag, |
| 6669 |   (int)mime_vec.len, |
| 6670 |   mime_vec.ptr, |
| 6671 |   cl, |
| 6672 |   suggest_connection_header(conn), |
| 6673 |   range, |
| 6674 |   encoding); |
| 6675 | |
| 6676 |  if (strcmp(conn->request_info.request_method, "HEAD") != 0) { |
| 6677 |    send_file_data(conn, filep, r1, cl); |
| 6678 |  } |
| 6679 |  mg_fclose(filep); |
| 6680 | } |
| 6681 | |
| 6682 | |
| 6683 | void |
| 6684 | mg_send_file(struct mg_connection *conn, const char *path) |
| 6685 | { |
| 6686 |  mg_send_mime_file(conn, path, NULL); |
| 6687 | } |
| 6688 | |
| 6689 | |
| 6690 | void |
| 6691 | mg_send_mime_file(struct mg_connection *conn, |
| 6692 | const char *path, |
| 6693 | const char *mime_type) |
| 6694 | { |
| 6695 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 6696 |  if (mg_stat(conn, path, &file)) { |
| 6697 |    if (file.is_directory) { |
| 6698 |      if (!conn) { |
| 6699 |        return; |
| 6700 |      } |
| 6701 |      if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], |
| 6702 |       "yes")) { |
| 6703 |        handle_directory_request(conn, path); |
| 6704 |      } else { |
| 6705 |        send_http_error(conn, |
| 6706 |         403, |
| 6707 |         "%s", |
| 6708 |         "Error: Directory listing denied"); |
| 6709 |      } |
| 6710 |    } else { |
| 6711 |      handle_static_file_request(conn, path, &file, mime_type); |
| 6712 |    } |
| 6713 |  } else { |
| 6714 |    send_http_error(conn, 404, "%s", "Error: File not found"); |
| 6715 |  } |
| 6716 | } |
| 6717 | |
| 6718 | |
| 6719 | /* For a given PUT path, create all intermediate subdirectories. |
| 6720 | * Return 0 if the path itself is a directory. |
| 6721 | * Return 1 if the path leads to a file. |
| 6722 | * Return -1 for if the path is too long. |
| 6723 | * Return -2 if path can not be created. |
| 6724 | */ |
| 6725 | static int |
| 6726 | put_dir(struct mg_connection *conn, const char *path) |
| 6727 | { |
| 6728 |  char buf[PATH_MAX]; |
| 6729 |  const char *s, *p; |
| 6730 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 6731 |  size_t len; |
| 6732 |  int res = 1; |
| 6733 | |
| 6734 |  for (s = p = path + 2; (p = strchr(s, '/')) != NULL; s = ++p) { |
| 6735 |    len = (size_t)(p - path); |
| 6736 |    if (len >= sizeof(buf)) { |
| 6737 |      /* path too long */ |
| 6738 |      res = -1; |
| 6739 |      break; |
| 6740 |    } |
| 6741 |    memcpy(buf, path, len); |
| 6742 |    buf[len] = '\0'; |
| 6743 | |
| 6744 |    /* Try to create intermediate directory */ |
| 6745 |    DEBUG_TRACE("mkdir(%s)", buf); |
| 6746 |    if (!mg_stat(conn, buf, &file) && mg_mkdir(conn, buf, 0755) != 0) { |
| 6747 |      /* path does not exixt and can not be created */ |
| 6748 |      res = -2; |
| 6749 |      break; |
| 6750 |    } |
| 6751 | |
| 6752 |    /* Is path itself a directory? */ |
| 6753 |    if (p[1] == '\0') { |
| 6754 |      res = 0; |
| 6755 |    } |
| 6756 |  } |
| 6757 | |
| 6758 |  return res; |
| 6759 | } |
| 6760 | |
| 6761 | |
| 6762 | static void |
| 6763 | remove_bad_file(const struct mg_connection *conn, const char *path) |
| 6764 | { |
| 6765 |  int r = mg_remove(conn, path); |
| 6766 |  if (r != 0) { |
| 6767 |    mg_cry(conn, "%s: Cannot remove invalid file %s", __func__, path); |
| 6768 |  } |
| 6769 | } |
| 6770 | |
| 6771 | |
| 6772 | long long |
| 6773 | mg_store_body(struct mg_connection *conn, const char *path) |
| 6774 | { |
| 6775 |  char buf[MG_BUF_LEN]; |
| 6776 |  long long len = 0; |
| 6777 |  int ret, n; |
| 6778 |  struct file fi; |
| 6779 | |
| 6780 |  if (conn->consumed_content != 0) { |
| 6781 |    mg_cry(conn, "%s: Contents already consumed", __func__); |
| 6782 |    return -11; |
| 6783 |  } |
| 6784 | |
| 6785 |  ret = put_dir(conn, path); |
| 6786 |  if (ret < 0) { |
| 6787 |    /* -1 for path too long, |
| 6788 |     * -2 for path can not be created. */ |
| 6789 |    return ret; |
| 6790 |  } |
| 6791 |  if (ret != 1) { |
| 6792 |    /* Return 0 means, path itself is a directory. */ |
| 6793 |    return 0; |
| 6794 |  } |
| 6795 | |
| 6796 |  if (mg_fopen(conn, path, "w", &fi) == 0) { |
| 6797 |    return -12; |
| 6798 |  } |
| 6799 | |
| 6800 |  ret = mg_read(conn, buf, sizeof(buf)); |
| 6801 |  while (ret > 0) { |
| 6802 |    n = (int)fwrite(buf, 1, (size_t)ret, fi.fp); |
| 6803 |    if (n != ret) { |
| 6804 |      mg_fclose(&fi); |
| 6805 |      remove_bad_file(conn, path); |
| 6806 |      return -13; |
| 6807 |    } |
| 6808 |    ret = mg_read(conn, buf, sizeof(buf)); |
| 6809 |  } |
| 6810 | |
| 6811 |  /* TODO: mg_fclose should return an error, |
| 6812 |   * and every caller should check and handle it. */ |
| 6813 |  if (fclose(fi.fp) != 0) { |
| 6814 |    remove_bad_file(conn, path); |
| 6815 |    return -14; |
| 6816 |  } |
| 6817 | |
| 6818 |  return len; |
| 6819 | } |
| 6820 | |
| 6821 | |
| 6822 | /* Parse HTTP headers from the given buffer, advance buffer to the point |
| 6823 | * where parsing stopped. */ |
| 6824 | static void |
| 6825 | parse_http_headers(char **buf, struct mg_request_info *ri) |
| 6826 | { |
| 6827 |  int i; |
| 6828 | |
| 6829 |  if (!ri) { |
| 6830 |    return; |
| 6831 |  } |
| 6832 | |
| 6833 |  ri->num_headers = 0; |
| 6834 | |
| 6835 |  for (i = 0; i < (int)ARRAY_SIZE(ri->http_headers); i++) { |
| 6836 |    char *dp = *buf; |
| 6837 |    while ((*dp != ':') && (*dp != '\r') && (*dp != 0)) { |
| 6838 |      dp++; |
| 6839 |    } |
| 6840 |    if (!*dp) { |
| 6841 |      /* neither : nor \r\n. This is not a valid field. */ |
| 6842 |      break; |
| 6843 |    } |
| 6844 |    if (*dp == '\r') { |
| 6845 |      if (dp[1] == '\n') { |
| 6846 |        /* \r\n */ |
| 6847 |        ri->http_headers[i].name = *buf; |
| 6848 |        ri->http_headers[i].value = 0; |
| 6849 |        *buf = dp; |
| 6850 |      } else { |
| 6851 |        /* stray \r. This is not valid. */ |
| 6852 |        break; |
| 6853 |      } |
| 6854 |    } else { |
| 6855 |      /* (*dp == ':') */ |
| 6856 |      *dp = 0; |
| 6857 |      ri->http_headers[i].name = *buf; |
| 6858 |      do { |
| 6859 |        dp++; |
| 6860 |      } while (*dp == ' '); |
| 6861 | |
| 6862 |      ri->http_headers[i].value = dp; |
| 6863 |      *buf = strstr(dp, "\r\n"); |
| 6864 |    } |
| 6865 | |
| 6866 |    ri->num_headers = i + 1; |
| 6867 |    if (*buf) { |
| 6868 |      (*buf)[0] = 0; |
| 6869 |      (*buf)[1] = 0; |
| 6870 |      *buf += 2; |
| 6871 |    } else { |
| 6872 |      *buf = dp; |
| 6873 |      break; |
| 6874 |    } |
| 6875 | |
| 6876 |    if (*buf[0] == '\r') { |
| 6877 |      /* This is the end of the header */ |
| 6878 |      break; |
| 6879 |    } |
| 6880 |  } |
| 6881 | } |
| 6882 | |
| 6883 | |
| 6884 | static int |
| 6885 | is_valid_http_method(const char *method) |
| 6886 | { |
| 6887 |  return !strcmp(method, "GET") /* HTTP (RFC 2616) */ |
| 6888 |   || !strcmp(method, "POST") /* HTTP (RFC 2616) */ |
| 6889 |   || !strcmp(method, "HEAD") /* HTTP (RFC 2616) */ |
| 6890 |   || !strcmp(method, "PUT") /* HTTP (RFC 2616) */ |
| 6891 |   || !strcmp(method, "DELETE") /* HTTP (RFC 2616) */ |
| 6892 |   || !strcmp(method, "OPTIONS") /* HTTP (RFC 2616) */ |
| 6893 |   /* TRACE method (RFC 2616) is not supported for security reasons */ |
| 6894 |   || !strcmp(method, "CONNECT") /* HTTP (RFC 2616) */ |
| 6895 | |
| 6896 |   || !strcmp(method, "PROPFIND") /* WEBDAV (RFC 2518) */ |
| 6897 |   || !strcmp(method, "MKCOL") /* WEBDAV (RFC 2518) */ |
| 6898 | |
| 6899 |   /* Unsupported WEBDAV Methods: */ |
| 6900 |   /* PROPPATCH, COPY, MOVE, LOCK, UNLOCK (RFC 2518) */ |
| 6901 |   /* + 11 methods from RFC 3253 */ |
| 6902 |   /* ORDERPATCH (RFC 3648) */ |
| 6903 |   /* ACL (RFC 3744) */ |
| 6904 |   /* SEARCH (RFC 5323) */ |
| 6905 |   /* + MicroSoft extensions |
| 6906 |   * https://msdn.microsoft.com/en-us/library/aa142917.aspx */ |
| 6907 | |
| 6908 |   /* PATCH method only allowed for CGI/Lua/LSP and callbacks. */ |
| 6909 |   || !strcmp(method, "PATCH"); /* PATCH method (RFC 5789) */ |
| 6910 | } |
| 6911 | |
| 6912 | |
| 6913 | /* Parse HTTP request, fill in mg_request_info structure. |
| 6914 | * This function modifies the buffer by NUL-terminating |
| 6915 | * HTTP request components, header names and header values. */ |
| 6916 | static int |
| 6917 | parse_http_message(char *buf, int len, struct mg_request_info *ri) |
| 6918 | { |
| 6919 |  int is_request, request_length; |
| 6920 | |
| 6921 |  if (!ri) { |
| 6922 |    return 0; |
| 6923 |  } |
| 6924 | |
| 6925 |  request_length = get_request_len(buf, len); |
| 6926 | |
| 6927 |  if (request_length > 0) { |
| 6928 |    /* Reset attributes. DO NOT TOUCH is_ssl, remote_ip, remote_addr, |
| 6929 |     * remote_port */ |
| 6930 |    ri->remote_user = ri->request_method = ri->request_uri = |
| 6931 |     ri->http_version = NULL; |
| 6932 |    ri->num_headers = 0; |
| 6933 | |
| 6934 |    buf[request_length - 1] = '\0'; |
| 6935 | |
| 6936 |    /* RFC says that all initial whitespaces should be ingored */ |
| 6937 |    while (*buf != '\0' && isspace(*(unsigned char *)buf)) { |
| 6938 |      buf++; |
| 6939 |    } |
| 6940 |    ri->request_method = skip(&buf, " "); |
| 6941 |    ri->request_uri = skip(&buf, " "); |
| 6942 |    ri->http_version = skip(&buf, "\r\n"); |
| 6943 | |
| 6944 |    /* HTTP message could be either HTTP request or HTTP response, e.g. |
| 6945 |     * "GET / HTTP/1.0 ...." or "HTTP/1.0 200 OK ..." */ |
| 6946 |    is_request = is_valid_http_method(ri->request_method); |
| 6947 |    if ((is_request && memcmp(ri->http_version, "HTTP/", 5) != 0) |
| 6948 |     || (!is_request && memcmp(ri->request_method, "HTTP/", 5) != 0)) { |
| 6949 |      request_length = -1; |
| 6950 |    } else { |
| 6951 |      if (is_request) { |
| 6952 |        ri->http_version += 5; |
| 6953 |      } |
| 6954 |      parse_http_headers(&buf, ri); |
| 6955 |    } |
| 6956 |  } |
| 6957 |  return request_length; |
| 6958 | } |
| 6959 | |
| 6960 | |
| 6961 | /* Keep reading the input (either opened file descriptor fd, or socket sock, |
| 6962 | * or SSL descriptor ssl) into buffer buf, until \r\n\r\n appears in the |
| 6963 | * buffer (which marks the end of HTTP request). Buffer buf may already |
| 6964 | * have some data. The length of the data is stored in nread. |
| 6965 | * Upon every read operation, increase nread by the number of bytes read. */ |
| 6966 | static int |
| 6967 | read_request(FILE *fp, |
| 6968 | struct mg_connection *conn, |
| 6969 | char *buf, |
| 6970 | int bufsiz, |
| 6971 | int *nread) |
| 6972 | { |
| 6973 |  int request_len, n = 0; |
| 6974 |  struct timespec last_action_time; |
| 6975 |  double request_timeout; |
| 6976 | |
| 6977 |  if (!conn) { |
| 6978 |    return 0; |
| 6979 |  } |
| 6980 | |
| 6981 |  memset(&last_action_time, 0, sizeof(last_action_time)); |
| 6982 | |
| 6983 |  if (conn->ctx->config[REQUEST_TIMEOUT]) { |
| 6984 |    /* value of request_timeout is in seconds, config in milliseconds */ |
| 6985 |    request_timeout = atof(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; |
| 6986 |  } else { |
| 6987 |    request_timeout = -1.0; |
| 6988 |  } |
| 6989 | |
| 6990 |  request_len = get_request_len(buf, *nread); |
| 6991 | |
| 6992 |  /* first time reading from this connection */ |
| 6993 |  clock_gettime(CLOCK_MONOTONIC, &last_action_time); |
| 6994 | |
| 6995 |  while ( |
| 6996 |   (conn->ctx->stop_flag == 0) && (*nread < bufsiz) && (request_len == 0) |
| 6997 |   && ((mg_difftimespec(&last_action_time, &(conn->req_time)) |
| 6998 |   <= request_timeout) || (request_timeout < 0)) |
| 6999 |   && ((n = pull(fp, conn, buf + *nread, bufsiz - *nread, request_timeout)) |
| 7000 |   > 0)) { |
| 7001 |    *nread += n; |
| 7002 |    /* assert(*nread <= bufsiz); */ |
| 7003 |    if (*nread > bufsiz) { |
| 7004 |      return -2; |
| 7005 |    } |
| 7006 |    request_len = get_request_len(buf, *nread); |
| 7007 |    if (request_timeout > 0.0) { |
| 7008 |      clock_gettime(CLOCK_MONOTONIC, &last_action_time); |
| 7009 |    } |
| 7010 |  } |
| 7011 | |
| 7012 |  return (request_len <= 0 && n <= 0) ? -1 : request_len; |
| 7013 | } |
| 7014 | |
| 7015 | #if !defined(NO_FILES) |
| 7016 | /* For given directory path, substitute it to valid index file. |
| 7017 | * Return 1 if index file has been found, 0 if not found. |
| 7018 | * If the file is found, it's stats is returned in stp. */ |
| 7019 | static int |
| 7020 | substitute_index_file(struct mg_connection *conn, |
| 7021 | char *path, |
| 7022 | size_t path_len, |
| 7023 | struct file *filep) |
| 7024 | { |
| 7025 |  if (conn && conn->ctx) { |
| 7026 |    const char *list = conn->ctx->config[INDEX_FILES]; |
| 7027 |    struct file file = STRUCT_FILE_INITIALIZER; |
| 7028 |    struct vec filename_vec; |
| 7029 |    size_t n = strlen(path); |
| 7030 |    int found = 0; |
| 7031 | |
| 7032 |    /* The 'path' given to us points to the directory. Remove all trailing |
| 7033 |     * directory separator characters from the end of the path, and |
| 7034 |     * then append single directory separator character. */ |
| 7035 |    while (n > 0 && path[n - 1] == '/') { |
| 7036 |      n--; |
| 7037 |    } |
| 7038 |    path[n] = '/'; |
| 7039 | |
| 7040 |    /* Traverse index files list. For each entry, append it to the given |
| 7041 |     * path and see if the file exists. If it exists, break the loop */ |
| 7042 |    while ((list = next_option(list, &filename_vec, NULL)) != NULL) { |
| 7043 |      /* Ignore too long entries that may overflow path buffer */ |
| 7044 |      if (filename_vec.len > path_len - (n + 2)) { |
| 7045 |        continue; |
| 7046 |      } |
| 7047 | |
| 7048 |      /* Prepare full path to the index file */ |
| 7049 |      mg_strlcpy(path + n + 1, filename_vec.ptr, filename_vec.len + 1); |
| 7050 | |
| 7051 |      /* Does it exist? */ |
| 7052 |      if (mg_stat(conn, path, &file)) { |
| 7053 |        /* Yes it does, break the loop */ |
| 7054 |        *filep = file; |
| 7055 |        found = 1; |
| 7056 |        break; |
| 7057 |      } |
| 7058 |    } |
| 7059 | |
| 7060 |    /* If no index file exists, restore directory path */ |
| 7061 |    if (!found) { |
| 7062 |      path[n] = '\0'; |
| 7063 |    } |
| 7064 | |
| 7065 |    return found; |
| 7066 |  } |
| 7067 |  return 0; |
| 7068 | } |
| 7069 | #endif |
| 7070 | |
| 7071 | |
| 7072 | #if !defined(NO_CACHING) |
| 7073 | /* Return True if we should reply 304 Not Modified. */ |
| 7074 | static int |
| 7075 | is_not_modified(const struct mg_connection *conn, const struct file *filep) |
| 7076 | { |
| 7077 |  char etag[64]; |
| 7078 |  const char *ims = mg_get_header(conn, "If-Modified-Since"); |
| 7079 |  const char *inm = mg_get_header(conn, "If-None-Match"); |
| 7080 |  construct_etag(etag, sizeof(etag), filep); |
| 7081 |  if (!filep) { |
| 7082 |    return 0; |
| 7083 |  } |
| 7084 |  return (inm != NULL && !mg_strcasecmp(etag, inm)) |
| 7085 |   || (ims != NULL && (filep->last_modified <= parse_date_string(ims))); |
| 7086 | } |
| 7087 | #endif /* !NO_CACHING */ |
| 7088 | |
| 7089 | |
| 7090 | #if !defined(NO_CGI) || !defined(NO_FILES) |
| 7091 | static int |
| 7092 | forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl) |
| 7093 | { |
| 7094 |  const char *expect, *body; |
| 7095 |  char buf[MG_BUF_LEN]; |
| 7096 |  int to_read, nread, success = 0; |
| 7097 |  int64_t buffered_len; |
| 7098 |  double timeout = -1.0; |
| 7099 | |
| 7100 |  if (!conn) { |
| 7101 |    return 0; |
| 7102 |  } |
| 7103 |  if (conn->ctx->config[REQUEST_TIMEOUT]) { |
| 7104 |    timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; |
| 7105 |  } |
| 7106 | |
| 7107 |  expect = mg_get_header(conn, "Expect"); |
| 7108 |  /* assert(fp != NULL); */ |
| 7109 |  if (!fp) { |
| 7110 |    send_http_error(conn, 500, "%s", "Error: NULL File"); |
| 7111 |    return 0; |
| 7112 |  } |
| 7113 | |
| 7114 |  if (conn->content_len == -1 && !conn->is_chunked) { |
| 7115 |    /* Content length is not specified by the client. */ |
| 7116 |    send_http_error(conn, |
| 7117 |     411, |
| 7118 |     "%s", |
| 7119 |     "Error: Client did not specify content length"); |
| 7120 |  } else if ((expect != NULL) |
| 7121 |   && (mg_strcasecmp(expect, "100-continue") != 0)) { |
| 7122 |    /* Client sent an "Expect: xyz" header and xyz is not 100-continue. */ |
| 7123 |    send_http_error(conn, |
| 7124 |     417, |
| 7125 |     "Error: Can not fulfill expectation %s", |
| 7126 |     expect); |
| 7127 |  } else { |
| 7128 |    if (expect != NULL) { |
| 7129 |      (void)mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); |
| 7130 |      conn->status_code = 100; |
| 7131 |    } else { |
| 7132 |      conn->status_code = 200; |
| 7133 |    } |
| 7134 | |
| 7135 |    buffered_len = (int64_t)(conn->data_len) - (int64_t)conn->request_len |
| 7136 |     - conn->consumed_content; |
| 7137 | |
| 7138 |    /* assert(buffered_len >= 0); */ |
| 7139 |    /* assert(conn->consumed_content == 0); */ |
| 7140 | |
| 7141 |    if ((buffered_len < 0) || (conn->consumed_content != 0)) { |
| 7142 |      send_http_error(conn, 500, "%s", "Error: Size mismatch"); |
| 7143 |      return 0; |
| 7144 |    } |
| 7145 | |
| 7146 |    if (buffered_len > 0) { |
| 7147 |      if ((int64_t)buffered_len > conn->content_len) { |
| 7148 |        buffered_len = (int)conn->content_len; |
| 7149 |      } |
| 7150 |      body = conn->buf + conn->request_len + conn->consumed_content; |
| 7151 |      push_all(conn->ctx, fp, sock, ssl, body, (int64_t)buffered_len); |
| 7152 |      conn->consumed_content += buffered_len; |
| 7153 |    } |
| 7154 | |
| 7155 |    nread = 0; |
| 7156 |    while (conn->consumed_content < conn->content_len) { |
| 7157 |      to_read = sizeof(buf); |
| 7158 |      if ((int64_t)to_read > conn->content_len - conn->consumed_content) { |
| 7159 |        to_read = (int)(conn->content_len - conn->consumed_content); |
| 7160 |      } |
| 7161 |      nread = pull(NULL, conn, buf, to_read, timeout); |
| 7162 |      if (nread <= 0 |
| 7163 |       || push_all(conn->ctx, fp, sock, ssl, buf, nread) != nread) { |
| 7164 |        break; |
| 7165 |      } |
| 7166 |      conn->consumed_content += nread; |
| 7167 |    } |
| 7168 | |
| 7169 |    if (conn->consumed_content == conn->content_len) { |
| 7170 |      success = (nread >= 0); |
| 7171 |    } |
| 7172 | |
| 7173 |    /* Each error code path in this function must send an error */ |
| 7174 |    if (!success) { |
| 7175 |      /* NOTE: Maybe some data has already been sent. */ |
| 7176 |      /* TODO (low): If some data has been sent, a correct error |
| 7177 |       * reply can no longer be sent, so just close the connection */ |
| 7178 |      send_http_error(conn, 500, "%s", ""); |
| 7179 |    } |
| 7180 |  } |
| 7181 | |
| 7182 |  return success; |
| 7183 | } |
| 7184 | #endif |
| 7185 | |
| 7186 | #if !defined(NO_CGI) |
| 7187 | /* This structure helps to create an environment for the spawned CGI program. |
| 7188 | * Environment is an array of "VARIABLE=VALUE\0" ASCIIZ strings, |
| 7189 | * last element must be NULL. |
| 7190 | * However, on Windows there is a requirement that all these VARIABLE=VALUE\0 |
| 7191 | * strings must reside in a contiguous buffer. The end of the buffer is |
| 7192 | * marked by two '\0' characters. |
| 7193 | * We satisfy both worlds: we create an envp array (which is vars), all |
| 7194 | * entries are actually pointers inside buf. */ |
| 7195 | struct cgi_environment { |
| 7196 |  struct mg_connection *conn; |
| 7197 |  /* Data block */ |
| 7198 |  char *buf; /* Environment buffer */ |
| 7199 |  size_t buflen; /* Space available in buf */ |
| 7200 |  size_t bufused; /* Space taken in buf */ |
| 7201 |   /* Index block */ |
| 7202 |  char **var; /* char **envp */ |
| 7203 |  size_t varlen; /* Number of variables available in var */ |
| 7204 |  size_t varused; /* Number of variables stored in var */ |
| 7205 | }; |
| 7206 | |
| 7207 | |
| 7208 | static void addenv(struct cgi_environment *env, |
| 7209 | PRINTF_FORMAT_STRING(const char *fmt), |
| 7210 | ...) PRINTF_ARGS(2, 3); |
| 7211 | |
| 7212 | /* Append VARIABLE=VALUE\0 string to the buffer, and add a respective |
| 7213 | * pointer into the vars array. Assumes env != NULL and fmt != NULL. */ |
| 7214 | static void |
| 7215 | addenv(struct cgi_environment *env, const char *fmt, ...) |
| 7216 | { |
| 7217 |  size_t n, space; |
| 7218 |  int truncated; |
| 7219 |  char *added; |
| 7220 |  va_list ap; |
| 7221 | |
| 7222 |  /* Calculate how much space is left in the buffer */ |
| 7223 |  space = (env->buflen - env->bufused); |
| 7224 | |
| 7225 |  /* Calculate an estimate for the required space */ |
| 7226 |  n = strlen(fmt) + 2 + 128; |
| 7227 | |
| 7228 |  do { |
| 7229 |    if (space <= n) { |
| 7230 |      /* Allocate new buffer */ |
| 7231 |      n = env->buflen + CGI_ENVIRONMENT_SIZE; |
| 7232 |      added = (char *)mg_realloc(env->buf, n); |
| 7233 |      if (!added) { |
| 7234 |        /* Out of memory */ |
| 7235 |        mg_cry(env->conn, |
| 7236 |         "%s: Cannot allocate memory for CGI variable [%s]", |
| 7237 |         __func__, |
| 7238 |         fmt); |
| 7239 |        return; |
| 7240 |      } |
| 7241 |      env->buf = added; |
| 7242 |      env->buflen = n; |
| 7243 |      space = (env->buflen - env->bufused); |
| 7244 |    } |
| 7245 | |
| 7246 |    /* Make a pointer to the free space int the buffer */ |
| 7247 |    added = env->buf + env->bufused; |
| 7248 | |
| 7249 |    /* Copy VARIABLE=VALUE\0 string into the free space */ |
| 7250 |    va_start(ap, fmt); |
| 7251 |    mg_vsnprintf(env->conn, &truncated, added, (size_t)space, fmt, ap); |
| 7252 |    va_end(ap); |
| 7253 | |
| 7254 |    /* Do not add truncated strings to the environment */ |
| 7255 |    if (truncated) { |
| 7256 |      /* Reallocate the buffer */ |
| 7257 |      space = 0; |
| 7258 |      n = 1; |
| 7259 |    } |
| 7260 |  } while (truncated); |
| 7261 | |
| 7262 |  /* Calculate number of bytes added to the environment */ |
| 7263 |  n = strlen(added) + 1; |
| 7264 |  env->bufused += n; |
| 7265 | |
| 7266 |  /* Now update the variable index */ |
| 7267 |  space = (env->varlen - env->varused); |
| 7268 |  if (space < 2) { |
| 7269 |    mg_cry(env->conn, |
| 7270 |     "%s: Cannot register CGI variable [%s]", |
| 7271 |     __func__, |
| 7272 |     fmt); |
| 7273 |    return; |
| 7274 |  } |
| 7275 | |
| 7276 |  /* Append a pointer to the added string into the envp array */ |
| 7277 |  env->var[env->varused] = added; |
| 7278 |  env->varused++; |
| 7279 | } |
| 7280 | |
| 7281 | |
| 7282 | static void |
| 7283 | prepare_cgi_environment(struct mg_connection *conn, |
| 7284 | const char *prog, |
| 7285 | struct cgi_environment *env) |
| 7286 | { |
| 7287 |  const char *s; |
| 7288 |  struct vec var_vec; |
| 7289 |  char *p, src_addr[IP_ADDR_STR_LEN], http_var_name[128]; |
| 7290 |  int i, truncated; |
| 7291 | |
| 7292 |  if (conn == NULL || prog == NULL || env == NULL) { |
| 7293 |    return; |
| 7294 |  } |
| 7295 | |
| 7296 |  env->conn = conn; |
| 7297 |  env->buflen = CGI_ENVIRONMENT_SIZE; |
| 7298 |  env->bufused = 0; |
| 7299 |  env->buf = (char *)mg_malloc(env->buflen); |
| 7300 |  env->varlen = MAX_CGI_ENVIR_VARS; |
| 7301 |  env->varused = 0; |
| 7302 |  env->var = (char **)mg_malloc(env->buflen * sizeof(char *)); |
| 7303 | |
| 7304 |  addenv(env, "SERVER_NAME=%s", conn->ctx->config[AUTHENTICATION_DOMAIN]); |
| 7305 |  addenv(env, "SERVER_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); |
| 7306 |  addenv(env, "DOCUMENT_ROOT=%s", conn->ctx->config[DOCUMENT_ROOT]); |
| 7307 |  addenv(env, "SERVER_SOFTWARE=%s/%s", "Civetweb", mg_version()); |
| 7308 | |
| 7309 |  /* Prepare the environment block */ |
| 7310 |  addenv(env, "%s", "GATEWAY_INTERFACE=CGI/1.1"); |
| 7311 |  addenv(env, "%s", "SERVER_PROTOCOL=HTTP/1.1"); |
| 7312 |  addenv(env, "%s", "REDIRECT_STATUS=200"); /* For PHP */ |
| 7313 | |
| 7314 | #if defined(USE_IPV6) |
| 7315 |  if (conn->client.lsa.sa.sa_family == AF_INET6) { |
| 7316 |    addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin6.sin6_port)); |
| 7317 |  } else |
| 7318 | #endif |
| 7319 |  { |
| 7320 |    addenv(env, "SERVER_PORT=%d", ntohs(conn->client.lsa.sin.sin_port)); |
| 7321 |  } |
| 7322 | |
| 7323 |  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); |
| 7324 |  addenv(env, "REMOTE_ADDR=%s", src_addr); |
| 7325 | |
| 7326 |  addenv(env, "REQUEST_METHOD=%s", conn->request_info.request_method); |
| 7327 |  addenv(env, "REMOTE_PORT=%d", conn->request_info.remote_port); |
| 7328 | |
| 7329 |  addenv(env, "REQUEST_URI=%s", conn->request_info.request_uri); |
| 7330 |  addenv(env, "LOCAL_URI=%s", conn->request_info.local_uri); |
| 7331 | |
| 7332 |  /* SCRIPT_NAME */ |
| 7333 |  addenv(env, |
| 7334 |   "SCRIPT_NAME=%.*s", |
| 7335 |   (int)strlen(conn->request_info.local_uri) |
| 7336 |   - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)), |
| 7337 |   conn->request_info.local_uri); |
| 7338 | |
| 7339 |  addenv(env, "SCRIPT_FILENAME=%s", prog); |
| 7340 |  if (conn->path_info == NULL) { |
| 7341 |    addenv(env, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]); |
| 7342 |  } else { |
| 7343 |    addenv(env, |
| 7344 |     "PATH_TRANSLATED=%s%s", |
| 7345 |     conn->ctx->config[DOCUMENT_ROOT], |
| 7346 |     conn->path_info); |
| 7347 |  } |
| 7348 | |
| 7349 |  addenv(env, "HTTPS=%s", conn->ssl == NULL ? "off" : "on"); |
| 7350 | |
| 7351 |  if ((s = mg_get_header(conn, "Content-Type")) != NULL) { |
| 7352 |    addenv(env, "CONTENT_TYPE=%s", s); |
| 7353 |  } |
| 7354 |  if (conn->request_info.query_string != NULL) { |
| 7355 |    addenv(env, "QUERY_STRING=%s", conn->request_info.query_string); |
| 7356 |  } |
| 7357 |  if ((s = mg_get_header(conn, "Content-Length")) != NULL) { |
| 7358 |    addenv(env, "CONTENT_LENGTH=%s", s); |
| 7359 |  } |
| 7360 |  if ((s = getenv("PATH")) != NULL) { |
| 7361 |    addenv(env, "PATH=%s", s); |
| 7362 |  } |
| 7363 |  if (conn->path_info != NULL) { |
| 7364 |    addenv(env, "PATH_INFO=%s", conn->path_info); |
| 7365 |  } |
| 7366 | |
| 7367 |  if (conn->status_code > 0) { |
| 7368 |    /* CGI error handler should show the status code */ |
| 7369 |    addenv(env, "STATUS=%d", conn->status_code); |
| 7370 |  } |
| 7371 | |
| 7372 | #if defined(_WIN32) |
| 7373 |  if ((s = getenv("COMSPEC")) != NULL) { |
| 7374 |    addenv(env, "COMSPEC=%s", s); |
| 7375 |  } |
| 7376 |  if ((s = getenv("SYSTEMROOT")) != NULL) { |
| 7377 |    addenv(env, "SYSTEMROOT=%s", s); |
| 7378 |  } |
| 7379 |  if ((s = getenv("SystemDrive")) != NULL) { |
| 7380 |    addenv(env, "SystemDrive=%s", s); |
| 7381 |  } |
| 7382 |  if ((s = getenv("ProgramFiles")) != NULL) { |
| 7383 |    addenv(env, "ProgramFiles=%s", s); |
| 7384 |  } |
| 7385 |  if ((s = getenv("ProgramFiles(x86)")) != NULL) { |
| 7386 |    addenv(env, "ProgramFiles(x86)=%s", s); |
| 7387 |  } |
| 7388 | #else |
| 7389 |  if ((s = getenv("LD_LIBRARY_PATH")) != NULL) { |
| 7390 |    addenv(env, "LD_LIBRARY_PATH=%s", s); |
| 7391 |  } |
| 7392 | #endif /* _WIN32 */ |
| 7393 | |
| 7394 |  if ((s = getenv("PERLLIB")) != NULL) { |
| 7395 |    addenv(env, "PERLLIB=%s", s); |
| 7396 |  } |
| 7397 | |
| 7398 |  if (conn->request_info.remote_user != NULL) { |
| 7399 |    addenv(env, "REMOTE_USER=%s", conn->request_info.remote_user); |
| 7400 |    addenv(env, "%s", "AUTH_TYPE=Digest"); |
| 7401 |  } |
| 7402 | |
| 7403 |  /* Add all headers as HTTP_* variables */ |
| 7404 |  for (i = 0; i < conn->request_info.num_headers; i++) { |
| 7405 | |
| 7406 |    (void)mg_snprintf(conn, |
| 7407 |     &truncated, |
| 7408 |     http_var_name, |
| 7409 |     sizeof(http_var_name), |
| 7410 |     "HTTP_%s", |
| 7411 |     conn->request_info.http_headers[i].name); |
| 7412 | |
| 7413 |    if (truncated) { |
| 7414 |      mg_cry(conn, |
| 7415 |       "%s: HTTP header variable too long [%s]", |
| 7416 |       __func__, |
| 7417 |       conn->request_info.http_headers[i].name); |
| 7418 |      continue; |
| 7419 |    } |
| 7420 | |
| 7421 |    /* Convert variable name into uppercase, and change - to _ */ |
| 7422 |    for (p = http_var_name; *p != '\0'; p++) { |
| 7423 |      if (*p == '-') { |
| 7424 |        *p = '_'; |
| 7425 |      } |
| 7426 |      *p = (char)toupper(*(unsigned char *)p); |
| 7427 |    } |
| 7428 | |
| 7429 |    addenv(env, |
| 7430 |     "%s=%s", |
| 7431 |     http_var_name, |
| 7432 |     conn->request_info.http_headers[i].value); |
| 7433 |  } |
| 7434 | |
| 7435 |  /* Add user-specified variables */ |
| 7436 |  s = conn->ctx->config[CGI_ENVIRONMENT]; |
| 7437 |  while ((s = next_option(s, &var_vec, NULL)) != NULL) { |
| 7438 |    addenv(env, "%.*s", (int)var_vec.len, var_vec.ptr); |
| 7439 |  } |
| 7440 | |
| 7441 |  env->var[env->varused] = NULL; |
| 7442 |  env->buf[env->bufused] = '\0'; |
| 7443 | } |
| 7444 | |
| 7445 | |
| 7446 | static void |
| 7447 | handle_cgi_request(struct mg_connection *conn, const char *prog) |
| 7448 | { |
| 7449 |  char *buf; |
| 7450 |  size_t buflen; |
| 7451 |  int headers_len, data_len, i, truncated; |
| 7452 |  int fdin[2] = {-1, -1}, fdout[2] = {-1, -1}, fderr[2] = {-1, -1}; |
| 7453 |  const char *status, *status_text, *connection_state; |
| 7454 |  char *pbuf, dir[PATH_MAX], *p; |
| 7455 |  struct mg_request_info ri; |
| 7456 |  struct cgi_environment blk; |
| 7457 |  FILE *in = NULL, *out = NULL, *err = NULL; |
| 7458 |  struct file fout = STRUCT_FILE_INITIALIZER; |
| 7459 |  pid_t pid = (pid_t)-1; |
| 7460 | |
| 7461 |  if (conn == NULL) { |
| 7462 |    return; |
| 7463 |  } |
| 7464 | |
| 7465 |  buf = NULL; |
| 7466 |  buflen = 16384; |
| 7467 |  prepare_cgi_environment(conn, prog, &blk); |
| 7468 | |
| 7469 |  /* CGI must be executed in its own directory. 'dir' must point to the |
| 7470 |   * directory containing executable program, 'p' must point to the |
| 7471 |   * executable program name relative to 'dir'. */ |
| 7472 |  (void)mg_snprintf(conn, &truncated, dir, sizeof(dir), "%s", prog); |
| 7473 | |
| 7474 |  if (truncated) { |
| 7475 |    mg_cry(conn, "Error: CGI program \"%s\": Path too long", prog); |
| 7476 |    send_http_error(conn, 500, "Error: %s", "CGI path too long"); |
| 7477 |    goto done; |
| 7478 |  } |
| 7479 | |
| 7480 |  if ((p = strrchr(dir, '/')) != NULL) { |
| 7481 |    *p++ = '\0'; |
| 7482 |  } else { |
| 7483 |    dir[0] = '.', dir[1] = '\0'; |
| 7484 |    p = (char *)prog; |
| 7485 |  } |
| 7486 | |
| 7487 |  if (pipe(fdin) != 0 || pipe(fdout) != 0 || pipe(fderr) != 0) { |
| 7488 |    status = strerror(ERRNO); |
| 7489 |    mg_cry(conn, |
| 7490 |     "Error: CGI program \"%s\": Can not create CGI pipes: %s", |
| 7491 |     prog, |
| 7492 |     status); |
| 7493 |    send_http_error(conn, 500, "Error: Cannot create CGI pipe: %s", status); |
| 7494 |    goto done; |
| 7495 |  } |
| 7496 | |
| 7497 |  pid = spawn_process(conn, p, blk.buf, blk.var, fdin, fdout, fderr, dir); |
| 7498 | |
| 7499 |  if (pid == (pid_t)-1) { |
| 7500 |    status = strerror(ERRNO); |
| 7501 |    mg_cry(conn, |
| 7502 |     "Error: CGI program \"%s\": Can not spawn CGI process: %s", |
| 7503 |     prog, |
| 7504 |     status); |
| 7505 |    send_http_error(conn, |
| 7506 |     500, |
| 7507 |     "Error: Cannot spawn CGI process [%s]: %s", |
| 7508 |     prog, |
| 7509 |     status); |
| 7510 |    goto done; |
| 7511 |  } |
| 7512 | |
| 7513 |  /* Make sure child closes all pipe descriptors. It must dup them to 0,1 */ |
| 7514 |  set_close_on_exec((SOCKET)fdin[0], conn); /* stdin read */ |
| 7515 |  set_close_on_exec((SOCKET)fdout[1], conn); /* stdout write */ |
| 7516 |  set_close_on_exec((SOCKET)fderr[1], conn); /* stderr write */ |
| 7517 |  set_close_on_exec((SOCKET)fdin[1], conn); /* stdin write */ |
| 7518 |  set_close_on_exec((SOCKET)fdout[0], conn); /* stdout read */ |
| 7519 |  set_close_on_exec((SOCKET)fderr[0], conn); /* stderr read */ |
| 7520 | |
| 7521 |  /* Parent closes only one side of the pipes. |
| 7522 |   * If we don't mark them as closed, close() attempt before |
| 7523 |   * return from this function throws an exception on Windows. |
| 7524 |   * Windows does not like when closed descriptor is closed again. */ |
| 7525 |  (void)close(fdin[0]); |
| 7526 |  (void)close(fdout[1]); |
| 7527 |  (void)close(fderr[1]); |
| 7528 |  fdin[0] = fdout[1] = fderr[1] = -1; |
| 7529 | |
| 7530 |  if ((in = fdopen(fdin[1], "wb")) == NULL) { |
| 7531 |    status = strerror(ERRNO); |
| 7532 |    mg_cry(conn, |
| 7533 |     "Error: CGI program \"%s\": Can not open stdin: %s", |
| 7534 |     prog, |
| 7535 |     status); |
| 7536 |    send_http_error(conn, |
| 7537 |     500, |
| 7538 |     "Error: CGI can not open fdin\nfopen: %s", |
| 7539 |     status); |
| 7540 |    goto done; |
| 7541 |  } |
| 7542 | |
| 7543 |  if ((out = fdopen(fdout[0], "rb")) == NULL) { |
| 7544 |    status = strerror(ERRNO); |
| 7545 |    mg_cry(conn, |
| 7546 |     "Error: CGI program \"%s\": Can not open stdout: %s", |
| 7547 |     prog, |
| 7548 |     status); |
| 7549 |    send_http_error(conn, |
| 7550 |     500, |
| 7551 |     "Error: CGI can not open fdout\nfopen: %s", |
| 7552 |     status); |
| 7553 |    goto done; |
| 7554 |  } |
| 7555 | |
| 7556 |  if ((err = fdopen(fderr[0], "rb")) == NULL) { |
| 7557 |    status = strerror(ERRNO); |
| 7558 |    mg_cry(conn, |
| 7559 |     "Error: CGI program \"%s\": Can not open stderr: %s", |
| 7560 |     prog, |
| 7561 |     status); |
| 7562 |    send_http_error(conn, |
| 7563 |     500, |
| 7564 |     "Error: CGI can not open fdout\nfopen: %s", |
| 7565 |     status); |
| 7566 |    goto done; |
| 7567 |  } |
| 7568 | |
| 7569 |  setbuf(in, NULL); |
| 7570 |  setbuf(out, NULL); |
| 7571 |  setbuf(err, NULL); |
| 7572 |  fout.fp = out; |
| 7573 | |
| 7574 |  if ((conn->request_info.content_length > 0) || conn->is_chunked) { |
| 7575 |    /* This is a POST/PUT request, or another request with body data. */ |
| 7576 |    if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) { |
| 7577 |      /* Error sending the body data */ |
| 7578 |      mg_cry(conn, |
| 7579 |       "Error: CGI program \"%s\": Forward body data failed", |
| 7580 |       prog); |
| 7581 |      goto done; |
| 7582 |    } |
| 7583 |  } |
| 7584 | |
| 7585 |  /* Close so child gets an EOF. */ |
| 7586 |  fclose(in); |
| 7587 |  in = NULL; |
| 7588 |  fdin[1] = -1; |
| 7589 | |
| 7590 |  /* Now read CGI reply into a buffer. We need to set correct |
| 7591 |   * status code, thus we need to see all HTTP headers first. |
| 7592 |   * Do not send anything back to client, until we buffer in all |
| 7593 |   * HTTP headers. */ |
| 7594 |  data_len = 0; |
| 7595 |  buf = (char *)mg_malloc(buflen); |
| 7596 |  if (buf == NULL) { |
| 7597 |    send_http_error(conn, |
| 7598 |     500, |
| 7599 |     "Error: Not enough memory for CGI buffer (%u bytes)", |
| 7600 |     (unsigned int)buflen); |
| 7601 |    mg_cry(conn, |
| 7602 |     "Error: CGI program \"%s\": Not enough memory for buffer (%u " |
| 7603 |     "bytes)", |
| 7604 |     prog, |
| 7605 |     (unsigned int)buflen); |
| 7606 |    goto done; |
| 7607 |  } |
| 7608 |  headers_len = read_request(out, conn, buf, (int)buflen, &data_len); |
| 7609 |  if (headers_len <= 0) { |
| 7610 | |
| 7611 |    /* Could not parse the CGI response. Check if some error message on |
| 7612 |     * stderr. */ |
| 7613 |    i = pull_all(err, conn, buf, (int)buflen); |
| 7614 |    if (i > 0) { |
| 7615 |      mg_cry(conn, |
| 7616 |       "Error: CGI program \"%s\" sent error " |
| 7617 |       "message: [%.*s]", |
| 7618 |       prog, |
| 7619 |       i, |
| 7620 |       buf); |
| 7621 |      send_http_error(conn, |
| 7622 |       500, |
| 7623 |       "Error: CGI program \"%s\" sent error " |
| 7624 |       "message: [%.*s]", |
| 7625 |       prog, |
| 7626 |       i, |
| 7627 |       buf); |
| 7628 |    } else { |
| 7629 |      mg_cry(conn, |
| 7630 |       "Error: CGI program sent malformed or too big " |
| 7631 |       "(>%u bytes) HTTP headers: [%.*s]", |
| 7632 |       (unsigned)buflen, |
| 7633 |       data_len, |
| 7634 |       buf); |
| 7635 | |
| 7636 |      send_http_error(conn, |
| 7637 |       500, |
| 7638 |       "Error: CGI program sent malformed or too big " |
| 7639 |       "(>%u bytes) HTTP headers: [%.*s]", |
| 7640 |       (unsigned)buflen, |
| 7641 |       data_len, |
| 7642 |       buf); |
| 7643 |    } |
| 7644 | |
| 7645 |    goto done; |
| 7646 |  } |
| 7647 |  pbuf = buf; |
| 7648 |  buf[headers_len - 1] = '\0'; |
| 7649 |  parse_http_headers(&pbuf, &ri); |
| 7650 | |
| 7651 |  /* Make up and send the status line */ |
| 7652 |  status_text = "OK"; |
| 7653 |  if ((status = get_header(&ri, "Status")) != NULL) { |
| 7654 |    conn->status_code = atoi(status); |
| 7655 |    status_text = status; |
| 7656 |    while (isdigit(*(const unsigned char *)status_text) |
| 7657 |     || *status_text == ' ') { |
| 7658 |      status_text++; |
| 7659 |    } |
| 7660 |  } else if (get_header(&ri, "Location") != NULL) { |
| 7661 |    conn->status_code = 302; |
| 7662 |  } else { |
| 7663 |    conn->status_code = 200; |
| 7664 |  } |
| 7665 |  connection_state = get_header(&ri, "Connection"); |
| 7666 |  if (!header_has_option(connection_state, "keep-alive")) { |
| 7667 |    conn->must_close = 1; |
| 7668 |  } |
| 7669 |  (void)mg_printf(conn, "HTTP/1.1 %d %s\r\n", conn->status_code, status_text); |
| 7670 | |
| 7671 |  /* Send headers */ |
| 7672 |  for (i = 0; i < ri.num_headers; i++) { |
| 7673 |    mg_printf(conn, |
| 7674 |     "%s: %s\r\n", |
| 7675 |     ri.http_headers[i].name, |
| 7676 |     ri.http_headers[i].value); |
| 7677 |  } |
| 7678 |  mg_write(conn, "\r\n", 2); |
| 7679 | |
| 7680 |  /* Send chunk of data that may have been read after the headers */ |
| 7681 |  conn->num_bytes_sent += |
| 7682 |   mg_write(conn, buf + headers_len, (size_t)(data_len - headers_len)); |
| 7683 | |
| 7684 |  /* Read the rest of CGI output and send to the client */ |
| 7685 |  send_file_data(conn, &fout, 0, INT64_MAX); |
| 7686 | |
| 7687 | done: |
| 7688 |  mg_free(blk.var); |
| 7689 |  mg_free(blk.buf); |
| 7690 | |
| 7691 |  if (pid != (pid_t)-1) { |
| 7692 |    kill(pid, SIGKILL); |
| 7693 | #if !defined(_WIN32) |
| 7694 |    { |
| 7695 |      int st; |
| 7696 |      while (waitpid(pid, &st, 0) != -1) |
| 7697 |       /* clean zombies */ |
| 7698 |    } |
| 7699 | #endif |
| 7700 |  } |
| 7701 |  if (fdin[0] != -1) { |
| 7702 |    close(fdin[0]); |
| 7703 |  } |
| 7704 |  if (fdout[1] != -1) { |
| 7705 |    close(fdout[1]); |
| 7706 |  } |
| 7707 | |
| 7708 |  if (in != NULL) { |
| 7709 |    fclose(in); |
| 7710 |  } else if (fdin[1] != -1) { |
| 7711 |    close(fdin[1]); |
| 7712 |  } |
| 7713 | |
| 7714 |  if (out != NULL) { |
| 7715 |    fclose(out); |
| 7716 |  } else if (fdout[0] != -1) { |
| 7717 |    close(fdout[0]); |
| 7718 |  } |
| 7719 | |
| 7720 |  if (err != NULL) { |
| 7721 |    fclose(err); |
| 7722 |  } else if (fderr[0] != -1) { |
| 7723 |    close(fderr[0]); |
| 7724 |  } |
| 7725 | |
| 7726 |  if (buf != NULL) { |
| 7727 |    mg_free(buf); |
| 7728 |  } |
| 7729 | } |
| 7730 | #endif /* !NO_CGI */ |
| 7731 | |
| 7732 | |
| 7733 | #if !defined(NO_FILES) |
| 7734 | static void |
| 7735 | mkcol(struct mg_connection *conn, const char *path) |
| 7736 | { |
| 7737 |  int rc, body_len; |
| 7738 |  struct de de; |
| 7739 |  char date[64]; |
| 7740 |  time_t curtime = time(NULL); |
| 7741 | |
| 7742 |  if (conn == NULL) { |
| 7743 |    return; |
| 7744 |  } |
| 7745 | |
| 7746 |  /* TODO (mid): Check the send_http_error situations in this function */ |
| 7747 | |
| 7748 |  memset(&de.file, 0, sizeof(de.file)); |
| 7749 |  if (!mg_stat(conn, path, &de.file)) { |
| 7750 |    mg_cry(conn, |
| 7751 |     "%s: mg_stat(%s) failed: %s", |
| 7752 |     __func__, |
| 7753 |     path, |
| 7754 |     strerror(ERRNO)); |
| 7755 |  } |
| 7756 | |
| 7757 |  if (de.file.last_modified) { |
| 7758 |    /* TODO (high): This check does not seem to make any sense ! */ |
| 7759 |    send_http_error( |
| 7760 |     conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); |
| 7761 |    return; |
| 7762 |  } |
| 7763 | |
| 7764 |  body_len = conn->data_len - conn->request_len; |
| 7765 |  if (body_len > 0) { |
| 7766 |    send_http_error( |
| 7767 |     conn, 415, "Error: mkcol(%s): %s", path, strerror(ERRNO)); |
| 7768 |    return; |
| 7769 |  } |
| 7770 | |
| 7771 |  rc = mg_mkdir(conn, path, 0755); |
| 7772 | |
| 7773 |  if (rc == 0) { |
| 7774 |    conn->status_code = 201; |
| 7775 |    gmt_time_string(date, sizeof(date), &curtime); |
| 7776 |    mg_printf(conn, |
| 7777 |     "HTTP/1.1 %d Created\r\n" |
| 7778 |     "Date: %s\r\n", |
| 7779 |     conn->status_code, |
| 7780 |     date); |
| 7781 |    send_static_cache_header(conn); |
| 7782 |    mg_printf(conn, |
| 7783 |     "Content-Length: 0\r\n" |
| 7784 |     "Connection: %s\r\n\r\n", |
| 7785 |     suggest_connection_header(conn)); |
| 7786 |  } else if (rc == -1) { |
| 7787 |    if (errno == EEXIST) { |
| 7788 |      send_http_error( |
| 7789 |       conn, 405, "Error: mkcol(%s): %s", path, strerror(ERRNO)); |
| 7790 |    } else if (errno == EACCES) { |
| 7791 |      send_http_error( |
| 7792 |       conn, 403, "Error: mkcol(%s): %s", path, strerror(ERRNO)); |
| 7793 |    } else if (errno == ENOENT) { |
| 7794 |      send_http_error( |
| 7795 |       conn, 409, "Error: mkcol(%s): %s", path, strerror(ERRNO)); |
| 7796 |    } else { |
| 7797 |      send_http_error(conn, 500, "fopen(%s): %s", path, strerror(ERRNO)); |
| 7798 |    } |
| 7799 |  } |
| 7800 | } |
| 7801 | |
| 7802 | |
| 7803 | static void |
| 7804 | put_file(struct mg_connection *conn, const char *path) |
| 7805 | { |
| 7806 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 7807 |  const char *range; |
| 7808 |  int64_t r1, r2; |
| 7809 |  int rc; |
| 7810 |  char date[64]; |
| 7811 |  time_t curtime = time(NULL); |
| 7812 | |
| 7813 |  if (conn == NULL) { |
| 7814 |    return; |
| 7815 |  } |
| 7816 | |
| 7817 |  if (mg_stat(conn, path, &file)) { |
| 7818 |    /* File already exists */ |
| 7819 |    conn->status_code = 200; |
| 7820 | |
| 7821 |    if (file.is_directory) { |
| 7822 |      /* This is an already existing directory, |
| 7823 |       * so there is nothing to do for the server. */ |
| 7824 |      rc = 0; |
| 7825 | |
| 7826 |    } else { |
| 7827 |      /* File exists and is not a directory. */ |
| 7828 |      /* Can it be replaced? */ |
| 7829 | |
| 7830 |      if (file.membuf != NULL) { |
| 7831 |        /* This is an "in-memory" file, that can not be replaced */ |
| 7832 |        send_http_error( |
| 7833 |         conn, |
| 7834 |         405, |
| 7835 |         "Error: Put not possible\nReplacing %s is not supported", |
| 7836 |         path); |
| 7837 |        return; |
| 7838 |      } |
| 7839 | |
| 7840 |      /* Check if the server may write this file */ |
| 7841 |      if (access(path, W_OK) == 0) { |
| 7842 |        /* Access granted */ |
| 7843 |        conn->status_code = 200; |
| 7844 |        rc = 1; |
| 7845 |      } else { |
| 7846 |        send_http_error( |
| 7847 |         conn, |
| 7848 |         403, |
| 7849 |         "Error: Put not possible\nReplacing %s is not allowed", |
| 7850 |         path); |
| 7851 |        return; |
| 7852 |      } |
| 7853 |    } |
| 7854 |  } else { |
| 7855 |    /* File should be created */ |
| 7856 |    conn->status_code = 201; |
| 7857 |    rc = put_dir(conn, path); |
| 7858 |  } |
| 7859 | |
| 7860 |  if (rc == 0) { |
| 7861 |    /* put_dir returns 0 if path is a directory */ |
| 7862 |    gmt_time_string(date, sizeof(date), &curtime); |
| 7863 |    mg_printf(conn, |
| 7864 |     "HTTP/1.1 %d %s\r\n", |
| 7865 |     conn->status_code, |
| 7866 |     mg_get_response_code_text(NULL, conn->status_code)); |
| 7867 |    send_no_cache_header(conn); |
| 7868 |    mg_printf(conn, |
| 7869 |     "Date: %s\r\n" |
| 7870 |     "Content-Length: 0\r\n" |
| 7871 |     "Connection: %s\r\n\r\n", |
| 7872 |     date, |
| 7873 |     suggest_connection_header(conn)); |
| 7874 | |
| 7875 |    /* Request to create a directory has been fulfilled successfully. |
| 7876 |     * No need to put a file. */ |
| 7877 |    return; |
| 7878 |  } |
| 7879 | |
| 7880 |  if (rc == -1) { |
| 7881 |    /* put_dir returns -1 if the path is too long */ |
| 7882 |    send_http_error(conn, |
| 7883 |     414, |
| 7884 |     "Error: Path too long\nput_dir(%s): %s", |
| 7885 |     path, |
| 7886 |     strerror(ERRNO)); |
| 7887 |    return; |
| 7888 |  } |
| 7889 | |
| 7890 |  if (rc == -2) { |
| 7891 |    /* put_dir returns -2 if the directory can not be created */ |
| 7892 |    send_http_error(conn, |
| 7893 |     500, |
| 7894 |     "Error: Can not create directory\nput_dir(%s): %s", |
| 7895 |     path, |
| 7896 |     strerror(ERRNO)); |
| 7897 |    return; |
| 7898 |  } |
| 7899 | |
| 7900 |  /* A file should be created or overwritten. */ |
| 7901 |  if (!mg_fopen(conn, path, "wb+", &file) || file.fp == NULL) { |
| 7902 |    mg_fclose(&file); |
| 7903 |    send_http_error(conn, |
| 7904 |     500, |
| 7905 |     "Error: Can not create file\nfopen(%s): %s", |
| 7906 |     path, |
| 7907 |     strerror(ERRNO)); |
| 7908 |    return; |
| 7909 |  } |
| 7910 | |
| 7911 |  fclose_on_exec(&file, conn); |
| 7912 |  range = mg_get_header(conn, "Content-Range"); |
| 7913 |  r1 = r2 = 0; |
| 7914 |  if (range != NULL && parse_range_header(range, &r1, &r2) > 0) { |
| 7915 |    conn->status_code = 206; /* Partial content */ |
| 7916 |    fseeko(file.fp, r1, SEEK_SET); |
| 7917 |  } |
| 7918 | |
| 7919 |  if (!forward_body_data(conn, file.fp, INVALID_SOCKET, NULL)) { |
| 7920 |    /* forward_body_data failed. |
| 7921 |     * The error code has already been sent to the client, |
| 7922 |     * and conn->status_code is already set. */ |
| 7923 |    mg_fclose(&file); |
| 7924 |    return; |
| 7925 |  } |
| 7926 | |
| 7927 |  gmt_time_string(date, sizeof(date), &curtime); |
| 7928 |  mg_printf(conn, |
| 7929 |   "HTTP/1.1 %d %s\r\n", |
| 7930 |   conn->status_code, |
| 7931 |   mg_get_response_code_text(NULL, conn->status_code)); |
| 7932 |  send_no_cache_header(conn); |
| 7933 |  mg_printf(conn, |
| 7934 |   "Date: %s\r\n" |
| 7935 |   "Content-Length: 0\r\n" |
| 7936 |   "Connection: %s\r\n\r\n", |
| 7937 |   date, |
| 7938 |   suggest_connection_header(conn)); |
| 7939 | |
| 7940 |  mg_fclose(&file); |
| 7941 | } |
| 7942 | |
| 7943 | |
| 7944 | static void |
| 7945 | delete_file(struct mg_connection *conn, const char *path) |
| 7946 | { |
| 7947 |  struct de de; |
| 7948 |  memset(&de.file, 0, sizeof(de.file)); |
| 7949 |  if (!mg_stat(conn, path, &de.file)) { |
| 7950 |    /* mg_stat returns 0 if the file does not exist */ |
| 7951 |    send_http_error(conn, |
| 7952 |     404, |
| 7953 |     "Error: Cannot delete file\nFile %s not found", |
| 7954 |     path); |
| 7955 |    return; |
| 7956 |  } |
| 7957 | |
| 7958 |  if (de.file.membuf != NULL) { |
| 7959 |    /* the file is cached in memory */ |
| 7960 |    send_http_error( |
| 7961 |     conn, |
| 7962 |     405, |
| 7963 |     "Error: Delete not possible\nDeleting %s is not supported", |
| 7964 |     path); |
| 7965 |    return; |
| 7966 |  } |
| 7967 | |
| 7968 |  if (de.file.is_directory) { |
| 7969 |    if (remove_directory(conn, path)) { |
| 7970 |      /* Delete is successful: Return 204 without content. */ |
| 7971 |      send_http_error(conn, 204, "%s", ""); |
| 7972 |    } else { |
| 7973 |      /* Delete is not successful: Return 500 (Server error). */ |
| 7974 |      send_http_error(conn, 500, "Error: Could not delete %s", path); |
| 7975 |    } |
| 7976 |    return; |
| 7977 |  } |
| 7978 | |
| 7979 |  /* This is an existing file (not a directory). |
| 7980 |   * Check if write permission is granted. */ |
| 7981 |  if (access(path, W_OK) != 0) { |
| 7982 |    /* File is read only */ |
| 7983 |    send_http_error( |
| 7984 |     conn, |
| 7985 |     403, |
| 7986 |     "Error: Delete not possible\nDeleting %s is not allowed", |
| 7987 |     path); |
| 7988 |    return; |
| 7989 |  } |
| 7990 | |
| 7991 |  /* Try to delete it. */ |
| 7992 |  if (mg_remove(conn, path) == 0) { |
| 7993 |    /* Delete was successful: Return 204 without content. */ |
| 7994 |    send_http_error(conn, 204, "%s", ""); |
| 7995 |  } else { |
| 7996 |    /* Delete not successful (file locked). */ |
| 7997 |    send_http_error(conn, |
| 7998 |     423, |
| 7999 |     "Error: Cannot delete file\nremove(%s): %s", |
| 8000 |     path, |
| 8001 |     strerror(ERRNO)); |
| 8002 |  } |
| 8003 | } |
| 8004 | #endif /* !NO_FILES */ |
| 8005 | |
| 8006 | |
| 8007 | static void |
| 8008 | send_ssi_file(struct mg_connection *, const char *, struct file *, int); |
| 8009 | |
| 8010 | |
| 8011 | static void |
| 8012 | do_ssi_include(struct mg_connection *conn, |
| 8013 | const char *ssi, |
| 8014 | char *tag, |
| 8015 | int include_level) |
| 8016 | { |
| 8017 |  char file_name[MG_BUF_LEN], path[512], *p; |
| 8018 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 8019 |  size_t len; |
| 8020 |  int truncated = 0; |
| 8021 | |
| 8022 |  if (conn == NULL) { |
| 8023 |    return; |
| 8024 |  } |
| 8025 | |
| 8026 |  /* sscanf() is safe here, since send_ssi_file() also uses buffer |
| 8027 |   * of size MG_BUF_LEN to get the tag. So strlen(tag) is |
| 8028 |   * always < MG_BUF_LEN. */ |
| 8029 |  if (sscanf(tag, " virtual=\"%511[^\"]\"", file_name) == 1) { |
| 8030 |    /* File name is relative to the webserver root */ |
| 8031 |    file_name[511] = 0; |
| 8032 |    (void)mg_snprintf(conn, |
| 8033 |     &truncated, |
| 8034 |     path, |
| 8035 |     sizeof(path), |
| 8036 |     "%s/%s", |
| 8037 |     conn->ctx->config[DOCUMENT_ROOT], |
| 8038 |     file_name); |
| 8039 | |
| 8040 |  } else if (sscanf(tag, " abspath=\"%511[^\"]\"", file_name) == 1) { |
| 8041 |    /* File name is relative to the webserver working directory |
| 8042 |     * or it is absolute system path */ |
| 8043 |    file_name[511] = 0; |
| 8044 |    (void) |
| 8045 |     mg_snprintf(conn, &truncated, path, sizeof(path), "%s", file_name); |
| 8046 | |
| 8047 |  } else if (sscanf(tag, " file=\"%511[^\"]\"", file_name) == 1 |
| 8048 |   || sscanf(tag, " \"%511[^\"]\"", file_name) == 1) { |
| 8049 |    /* File name is relative to the currect document */ |
| 8050 |    file_name[511] = 0; |
| 8051 |    (void)mg_snprintf(conn, &truncated, path, sizeof(path), "%s", ssi); |
| 8052 | |
| 8053 |    if (!truncated) { |
| 8054 |      if ((p = strrchr(path, '/')) != NULL) { |
| 8055 |        p[1] = '\0'; |
| 8056 |      } |
| 8057 |      len = strlen(path); |
| 8058 |      (void)mg_snprintf(conn, |
| 8059 |       &truncated, |
| 8060 |       path + len, |
| 8061 |       sizeof(path) - len, |
| 8062 |       "%s", |
| 8063 |       file_name); |
| 8064 |    } |
| 8065 | |
| 8066 |  } else { |
| 8067 |    mg_cry(conn, "Bad SSI #include: [%s]", tag); |
| 8068 |    return; |
| 8069 |  } |
| 8070 | |
| 8071 |  if (truncated) { |
| 8072 |    mg_cry(conn, "SSI #include path length overflow: [%s]", tag); |
| 8073 |    return; |
| 8074 |  } |
| 8075 | |
| 8076 |  if (!mg_fopen(conn, path, "rb", &file)) { |
| 8077 |    mg_cry(conn, |
| 8078 |     "Cannot open SSI #include: [%s]: fopen(%s): %s", |
| 8079 |     tag, |
| 8080 |     path, |
| 8081 |     strerror(ERRNO)); |
| 8082 |  } else { |
| 8083 |    fclose_on_exec(&file, conn); |
| 8084 |    if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], |
| 8085 |     strlen(conn->ctx->config[SSI_EXTENSIONS]), |
| 8086 |     path) > 0) { |
| 8087 |      send_ssi_file(conn, path, &file, include_level + 1); |
| 8088 |    } else { |
| 8089 |      send_file_data(conn, &file, 0, INT64_MAX); |
| 8090 |    } |
| 8091 |    mg_fclose(&file); |
| 8092 |  } |
| 8093 | } |
| 8094 | |
| 8095 | |
| 8096 | #if !defined(NO_POPEN) |
| 8097 | static void |
| 8098 | do_ssi_exec(struct mg_connection *conn, char *tag) |
| 8099 | { |
| 8100 |  char cmd[1024] = ""; |
| 8101 |  struct file file = STRUCT_FILE_INITIALIZER; |
| 8102 | |
| 8103 |  if (sscanf(tag, " \"%1023[^\"]\"", cmd) != 1) { |
| 8104 |    mg_cry(conn, "Bad SSI #exec: [%s]", tag); |
| 8105 |  } else { |
| 8106 |    cmd[1023] = 0; |
| 8107 |    if ((file.fp = popen(cmd, "r")) == NULL) { |
| 8108 |      mg_cry(conn, "Cannot SSI #exec: [%s]: %s", cmd, strerror(ERRNO)); |
| 8109 |    } else { |
| 8110 |      send_file_data(conn, &file, 0, INT64_MAX); |
| 8111 |      pclose(file.fp); |
| 8112 |    } |
| 8113 |  } |
| 8114 | } |
| 8115 | #endif /* !NO_POPEN */ |
| 8116 | |
| 8117 | |
| 8118 | static int |
| 8119 | mg_fgetc(struct file *filep, int offset) |
| 8120 | { |
| 8121 |  if (filep == NULL) { |
| 8122 |    return EOF; |
| 8123 |  } |
| 8124 |  if ( |
| 8125 |   ) { |
| 8126 |    return ((const unsigned char *)filep->membuf)[offset]; |
| 8127 |  } else if (filep->fp != NULL) { |
| 8128 |    return fgetc(filep->fp); |
| 8129 |  } else { |
| 8130 |    return EOF; |
| 8131 |  } |
| 8132 | } |
| 8133 | |
| 8134 | |
| 8135 | static void |
| 8136 | send_ssi_file(struct mg_connection *conn, |
| 8137 | const char *path, |
| 8138 | struct file *filep, |
| 8139 | int include_level) |
| 8140 | { |
| 8141 |  char buf[MG_BUF_LEN]; |
| 8142 |  int ch, offset, len, in_ssi_tag; |
| 8143 | |
| 8144 |  if (include_level > 10) { |
| 8145 |    mg_cry(conn, "SSI #include level is too deep (%s)", path); |
| 8146 |    return; |
| 8147 |  } |
| 8148 | |
| 8149 |  in_ssi_tag = len = offset = 0; |
| 8150 |  while ((ch = mg_fgetc(filep, offset)) != EOF) { |
| 8151 |    if (in_ssi_tag && ch == '>') { |
| 8152 |      in_ssi_tag = 0; |
| 8153 |      buf[len++] = (char)ch; |
| 8154 |      buf[len] = '\0'; |
| 8155 |      /* assert(len <= (int) sizeof(buf)); */ |
| 8156 |      if (len > (int)sizeof(buf)) { |
| 8157 |        break; |
| 8158 |      } |
| 8159 |      if (len < 6 || memcmp(buf, "<!--#", 5) != 0) { |
| 8160 |        /* Not an SSI tag, pass it */ |
| 8161 |        (void)mg_write(conn, buf, (size_t)len); |
| 8162 |      } else { |
| 8163 |        if (!memcmp(buf + 5, "include", 7)) { |
| 8164 |          do_ssi_include(conn, path, buf + 12, include_level); |
| 8165 | #if !defined(NO_POPEN) |
| 8166 |        } else if (!memcmp(buf + 5, "exec", 4)) { |
| 8167 |          do_ssi_exec(conn, buf + 9); |
| 8168 | #endif /* !NO_POPEN */ |
| 8169 |        } else { |
| 8170 |          mg_cry(conn, |
| 8171 |           "%s: unknown SSI " |
| 8172 |           "command: \"%s\"", |
| 8173 |           path, |
| 8174 |           buf); |
| 8175 |        } |
| 8176 |      } |
| 8177 |      len = 0; |
| 8178 |    } else if (in_ssi_tag) { |
| 8179 |      if (len == 5 && memcmp(buf, "<!--#", 5) != 0) { |
| 8180 |        /* Not an SSI tag */ |
| 8181 |        in_ssi_tag = 0; |
| 8182 |      } else if (len == (int)sizeof(buf) - 2) { |
| 8183 |        mg_cry(conn, "%s: SSI tag is too large", path); |
| 8184 |        len = 0; |
| 8185 |      } |
| 8186 |      buf[len++] = (char)(ch & 0xff); |
| 8187 |    } else if (ch == '<') { |
| 8188 |      in_ssi_tag = 1; |
| 8189 |      if (len > 0) { |
| 8190 |        mg_write(conn, buf, (size_t)len); |
| 8191 |      } |
| 8192 |      len = 0; |
| 8193 |      buf[len++] = (char)(ch & 0xff); |
| 8194 |    } else { |
| 8195 |      buf[len++] = (char)(ch & 0xff); |
| 8196 |      if (len == (int)sizeof(buf)) { |
| 8197 |        mg_write(conn, buf, (size_t)len); |
| 8198 |        len = 0; |
| 8199 |      } |
| 8200 |    } |
| 8201 |  } |
| 8202 | |
| 8203 |  /* Send the rest of buffered data */ |
| 8204 |  if (len > 0) { |
| 8205 |    mg_write(conn, buf, (size_t)len); |
| 8206 |  } |
| 8207 | } |
| 8208 | |
| 8209 | |
| 8210 | static void |
| 8211 | handle_ssi_file_request(struct mg_connection *conn, |
| 8212 | const char *path, |
| 8213 | struct file *filep) |
| 8214 | { |
| 8215 |  char date[64]; |
| 8216 |  time_t curtime = time(NULL); |
| 8217 |  const char *cors1, *cors2, *cors3; |
| 8218 | |
| 8219 |  if (conn == NULL || path == NULL || filep == NULL) { |
| 8220 |    return; |
| 8221 |  } |
| 8222 | |
| 8223 |  if (mg_get_header(conn, "Origin")) { |
| 8224 |    /* Cross-origin resource sharing (CORS). */ |
| 8225 |    cors1 = "Access-Control-Allow-Origin: "; |
| 8226 |    cors2 = conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN]; |
| 8227 |    cors3 = "\r\n"; |
| 8228 |  } else { |
| 8229 |    cors1 = cors2 = cors3 = ""; |
| 8230 |  } |
| 8231 | |
| 8232 |  if (!mg_fopen(conn, path, "rb", filep)) { |
| 8233 |    /* File exists (precondition for calling this function), |
| 8234 |     * but can not be opened by the server. */ |
| 8235 |    send_http_error(conn, |
| 8236 |     500, |
| 8237 |     "Error: Cannot read file\nfopen(%s): %s", |
| 8238 |     path, |
| 8239 |     strerror(ERRNO)); |
| 8240 |  } else { |
| 8241 |    conn->must_close = 1; |
| 8242 |    gmt_time_string(date, sizeof(date), &curtime); |
| 8243 |    fclose_on_exec(filep, conn); |
| 8244 |    mg_printf(conn, "HTTP/1.1 200 OK\r\n"); |
| 8245 |    send_no_cache_header(conn); |
| 8246 |    mg_printf(conn, |
| 8247 |     "%s%s%s" |
| 8248 |     "Date: %s\r\n" |
| 8249 |     "Content-Type: text/html\r\n" |
| 8250 |     "Connection: %s\r\n\r\n", |
| 8251 |     cors1, |
| 8252 |     cors2, |
| 8253 |     cors3, |
| 8254 |     date, |
| 8255 |     suggest_connection_header(conn)); |
| 8256 |    send_ssi_file(conn, path, filep, 0); |
| 8257 |    mg_fclose(filep); |
| 8258 |  } |
| 8259 | } |
| 8260 | |
| 8261 | |
| 8262 | #if !defined(NO_FILES) |
| 8263 | static void |
| 8264 | send_options(struct mg_connection *conn) |
| 8265 | { |
| 8266 |  char date[64]; |
| 8267 |  time_t curtime = time(NULL); |
| 8268 | |
| 8269 |  if (!conn) { |
| 8270 |    return; |
| 8271 |  } |
| 8272 | |
| 8273 |  conn->status_code = 200; |
| 8274 |  conn->must_close = 1; |
| 8275 |  gmt_time_string(date, sizeof(date), &curtime); |
| 8276 | |
| 8277 |  mg_printf(conn, |
| 8278 |   "HTTP/1.1 200 OK\r\n" |
| 8279 |   "Date: %s\r\n" |
| 8280 |   /* TODO: "Cache-Control" (?) */ |
| 8281 |   "Connection: %s\r\n" |
| 8282 |   "Allow: GET, POST, HEAD, CONNECT, PUT, DELETE, OPTIONS, " |
| 8283 |   "PROPFIND, MKCOL\r\n" |
| 8284 |   "DAV: 1\r\n\r\n", |
| 8285 |   date, |
| 8286 |   suggest_connection_header(conn)); |
| 8287 | } |
| 8288 | |
| 8289 | |
| 8290 | /* Writes PROPFIND properties for a collection element */ |
| 8291 | static void |
| 8292 | print_props(struct mg_connection *conn, const char *uri, struct file *filep) |
| 8293 | { |
| 8294 |  char mtime[64]; |
| 8295 | |
| 8296 |  if (conn == NULL || uri == NULL || filep == NULL) { |
| 8297 |    return; |
| 8298 |  } |
| 8299 | |
| 8300 |  gmt_time_string(mtime, sizeof(mtime), &filep->last_modified); |
| 8301 |  conn->num_bytes_sent += |
| 8302 |   mg_printf(conn, |
| 8303 |   "<d:response>" |
| 8304 |   "<d:href>%s</d:href>" |
| 8305 |   "<d:propstat>" |
| 8306 |   "<d:prop>" |
| 8307 |   "<d:resourcetype>%s</d:resourcetype>" |
| 8308 |   "<d:getcontentlength>%" INT64_FMT "</d:getcontentlength>" |
| 8309 |   "<d:getlastmodified>%s</d:getlastmodified>" |
| 8310 |   "</d:prop>" |
| 8311 |   "<d:status>HTTP/1.1 200 OK</d:status>" |
| 8312 |   "</d:propstat>" |
| 8313 |   "</d:response>\n", |
| 8314 |   uri, |
| 8315 |   filep->is_directory ? "<d:collection/>" : "", |
| 8316 |   filep->size, |
| 8317 |   mtime); |
| 8318 | } |
| 8319 | |
| 8320 | |
| 8321 | static void |
| 8322 | print_dav_dir_entry(struct de *de, void *data) |
| 8323 | { |
| 8324 |  char href[PATH_MAX]; |
| 8325 |  char href_encoded[PATH_MAX]; |
| 8326 |  int truncated; |
| 8327 | |
| 8328 |  struct mg_connection *conn = (struct mg_connection *)data; |
| 8329 |  if (!de || !conn) { |
| 8330 |    return; |
| 8331 |  } |
| 8332 |  mg_snprintf(conn, |
| 8333 |   &truncated, |
| 8334 |   href, |
| 8335 |   sizeof(href), |
| 8336 |   "%s%s", |
| 8337 |   conn->request_info.local_uri, |
| 8338 |   de->file_name); |
| 8339 | |
| 8340 |  if (!truncated) { |
| 8341 |    mg_url_encode(href, href_encoded, PATH_MAX - 1); |
| 8342 |    print_props(conn, href_encoded, &de->file); |
| 8343 |  } |
| 8344 | } |
| 8345 | |
| 8346 | |
| 8347 | static void |
| 8348 | handle_propfind(struct mg_connection *conn, |
| 8349 | const char *path, |
| 8350 | struct file *filep) |
| 8351 | { |
| 8352 |  const char *depth = mg_get_header(conn, "Depth"); |
| 8353 |  char date[64]; |
| 8354 |  time_t curtime = time(NULL); |
| 8355 | |
| 8356 |  gmt_time_string(date, sizeof(date), &curtime); |
| 8357 | |
| 8358 |  if (!conn || !path || !filep || !conn->ctx) { |
| 8359 |    return; |
| 8360 |  } |
| 8361 | |
| 8362 |  conn->must_close = 1; |
| 8363 |  conn->status_code = 207; |
| 8364 |  mg_printf(conn, |
| 8365 |   "HTTP/1.1 207 Multi-Status\r\n" |
| 8366 |   "Date: %s\r\n", |
| 8367 |   date); |
| 8368 |  send_static_cache_header(conn); |
| 8369 |  mg_printf(conn, |
| 8370 |   "Connection: %s\r\n" |
| 8371 |   "Content-Type: text/xml; charset=utf-8\r\n\r\n", |
| 8372 |   suggest_connection_header(conn)); |
| 8373 | |
| 8374 |  conn->num_bytes_sent += |
| 8375 |   mg_printf(conn, |
| 8376 |   "<?xml version=\"1.0\" encoding=\"utf-8\"?>" |
| 8377 |   "<d:multistatus xmlns:d='DAV:'>\n"); |
| 8378 | |
| 8379 |  /* Print properties for the requested resource itself */ |
| 8380 |  print_props(conn, conn->request_info.local_uri, filep); |
| 8381 | |
| 8382 |  /* If it is a directory, print directory entries too if Depth is not 0 */ |
| 8383 |  if (filep && filep->is_directory |
| 8384 |   && !mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], "yes") |
| 8385 |   && (depth == NULL || strcmp(depth, "0") != 0)) { |
| 8386 |    scan_directory(conn, path, conn, &print_dav_dir_entry); |
| 8387 |  } |
| 8388 | |
| 8389 |  conn->num_bytes_sent += mg_printf(conn, "%s\n", "</d:multistatus>"); |
| 8390 | } |
| 8391 | #endif |
| 8392 | |
| 8393 | void |
| 8394 | mg_lock_connection(struct mg_connection *conn) |
| 8395 | { |
| 8396 |  if (conn) { |
| 8397 |    (void)pthread_mutex_lock(&conn->mutex); |
| 8398 |  } |
| 8399 | } |
| 8400 | |
| 8401 | void |
| 8402 | mg_unlock_connection(struct mg_connection *conn) |
| 8403 | { |
| 8404 |  if (conn) { |
| 8405 |    (void)pthread_mutex_unlock(&conn->mutex); |
| 8406 |  } |
| 8407 | } |
| 8408 | |
| 8409 | void |
| 8410 | mg_lock_context(struct mg_context *ctx) |
| 8411 | { |
| 8412 |  if (ctx) { |
| 8413 |    (void)pthread_mutex_lock(&ctx->nonce_mutex); |
| 8414 |  } |
| 8415 | } |
| 8416 | |
| 8417 | void |
| 8418 | mg_unlock_context(struct mg_context *ctx) |
| 8419 | { |
| 8420 |  if (ctx) { |
| 8421 |    (void)pthread_mutex_unlock(&ctx->nonce_mutex); |
| 8422 |  } |
| 8423 | } |
| 8424 | |
| 8425 | #if defined(USE_TIMERS) |
| 8426 | #include "timer.inl" |
| 8427 | #endif /* USE_TIMERS */ |
| 8428 | |
| 8429 | #ifdef USE_LUA |
| 8430 | #include "mod_lua.inl" |
| 8431 | #endif /* USE_LUA */ |
| 8432 | |
| 8433 | #ifdef USE_DUKTAPE |
| 8434 | #include "mod_duktape.inl" |
| 8435 | #endif /* USE_DUKTAPE */ |
| 8436 | |
| 8437 | #if defined(USE_WEBSOCKET) |
| 8438 | |
| 8439 | /* START OF SHA-1 code |
| 8440 | * Copyright(c) By Steve Reid <steve@edmweb.com> */ |
| 8441 | #define SHA1HANDSOFF |
| 8442 | |
| 8443 | /* According to current tests (May 2015), the <solarisfixes.h> is not required. |
| 8444 | * |
| 8445 | * #if defined(__sun) |
| 8446 | * #include "solarisfixes.h" |
| 8447 | * #endif |
| 8448 | */ |
| 8449 | |
| 8450 | |
| 8451 | static int |
| 8452 | is_big_endian(void) |
| 8453 | { |
| 8454 |  static const int n = 1; |
| 8455 |  return ((char *)&n)[0] == 0; |
| 8456 | } |
| 8457 | |
| 8458 | |
| 8459 | union char64long16 { |
| 8460 |  unsigned char c[64]; |
| 8461 |  uint32_t l[16]; |
| 8462 | }; |
| 8463 | |
| 8464 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) |
| 8465 | |
| 8466 | |
| 8467 | static uint32_t |
| 8468 | blk0(union char64long16 *block, int i) |
| 8469 | { |
| 8470 |  /* Forrest: SHA expect BIG_ENDIAN, swap if LITTLE_ENDIAN */ |
| 8471 |  if (!is_big_endian()) { |
| 8472 |    block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) |
| 8473 |     | (rol(block->l[i], 8) & 0x00FF00FF); |
| 8474 |  } |
| 8475 |  return block->l[i]; |
| 8476 | } |
| 8477 | |
| 8478 | #define blk(i) \ |
| 8479 |  (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ block->l[(i + 8) & 15] \ |
| 8480 |   ^ block->l[(i + 2) & 15] ^ block->l[i & 15], \ |
| 8481 |   1)) |
| 8482 | #define R0(v, w, x, y, z, i) \ |
| 8483 |  z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + rol(v, 5); \ |
| 8484 |  w = rol(w, 30); |
| 8485 | #define R1(v, w, x, y, z, i) \ |
| 8486 |  z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ |
| 8487 |  w = rol(w, 30); |
| 8488 | #define R2(v, w, x, y, z, i) \ |
| 8489 |  z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \ |
| 8490 |  w = rol(w, 30); |
| 8491 | #define R3(v, w, x, y, z, i) \ |
| 8492 |  z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ |
| 8493 |  w = rol(w, 30); |
| 8494 | #define R4(v, w, x, y, z, i) \ |
| 8495 |  z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ |
| 8496 |  w = rol(w, 30); |
| 8497 | |
| 8498 | |
| 8499 | typedef struct { |
| 8500 |  uint32_t state[5]; |
| 8501 |  uint32_t count[2]; |
| 8502 |  unsigned char buffer[64]; |
| 8503 | } SHA1_CTX; |
| 8504 | |
| 8505 | |
| 8506 | static void |
| 8507 | SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) |
| 8508 | { |
| 8509 |  uint32_t a, b, c, d, e; |
| 8510 |  union char64long16 block[1]; |
| 8511 | |
| 8512 |  memcpy(block, buffer, 64); |
| 8513 |  a = state[0]; |
| 8514 |  b = state[1]; |
| 8515 |  c = state[2]; |
| 8516 |  d = state[3]; |
| 8517 |  e = state[4]; |
| 8518 |  R0(a, b, c, d, e, 0); |
| 8519 |  R0(e, a, b, c, d, 1); |
| 8520 |  R0(d, e, a, b, c, 2); |
| 8521 |  R0(c, d, e, a, b, 3); |
| 8522 |  R0(b, c, d, e, a, 4); |
| 8523 |  R0(a, b, c, d, e, 5); |
| 8524 |  R0(e, a, b, c, d, 6); |
| 8525 |  R0(d, e, a, b, c, 7); |
| 8526 |  R0(c, d, e, a, b, 8); |
| 8527 |  R0(b, c, d, e, a, 9); |
| 8528 |  R0(a, b, c, d, e, 10); |
| 8529 |  R0(e, a, b, c, d, 11); |
| 8530 |  R0(d, e, a, b, c, 12); |
| 8531 |  R0(c, d, e, a, b, 13); |
| 8532 |  R0(b, c, d, e, a, 14); |
| 8533 |  R0(a, b, c, d, e, 15); |
| 8534 |  R1(e, a, b, c, d, 16); |
| 8535 |  R1(d, e, a, b, c, 17); |
| 8536 |  R1(c, d, e, a, b, 18); |
| 8537 |  R1(b, c, d, e, a, 19); |
| 8538 |  R2(a, b, c, d, e, 20); |
| 8539 |  R2(e, a, b, c, d, 21); |
| 8540 |  R2(d, e, a, b, c, 22); |
| 8541 |  R2(c, d, e, a, b, 23); |
| 8542 |  R2(b, c, d, e, a, 24); |
| 8543 |  R2(a, b, c, d, e, 25); |
| 8544 |  R2(e, a, b, c, d, 26); |
| 8545 |  R2(d, e, a, b, c, 27); |
| 8546 |  R2(c, d, e, a, b, 28); |
| 8547 |  R2(b, c, d, e, a, 29); |
| 8548 |  R2(a, b, c, d, e, 30); |
| 8549 |  R2(e, a, b, c, d, 31); |
| 8550 |  R2(d, e, a, b, c, 32); |
| 8551 |  R2(c, d, e, a, b, 33); |
| 8552 |  R2(b, c, d, e, a, 34); |
| 8553 |  R2(a, b, c, d, e, 35); |
| 8554 |  R2(e, a, b, c, d, 36); |
| 8555 |  R2(d, e, a, b, c, 37); |
| 8556 |  R2(c, d, e, a, b, 38); |
| 8557 |  R2(b, c, d, e, a, 39); |
| 8558 |  R3(a, b, c, d, e, 40); |
| 8559 |  R3(e, a, b, c, d, 41); |
| 8560 |  R3(d, e, a, b, c, 42); |
| 8561 |  R3(c, d, e, a, b, 43); |
| 8562 |  R3(b, c, d, e, a, 44); |
| 8563 |  R3(a, b, c, d, e, 45); |
| 8564 |  R3(e, a, b, c, d, 46); |
| 8565 |  R3(d, e, a, b, c, 47); |
| 8566 |  R3(c, d, e, a, b, 48); |
| 8567 |  R3(b, c, d, e, a, 49); |
| 8568 |  R3(a, b, c, d, e, 50); |
| 8569 |  R3(e, a, b, c, d, 51); |
| 8570 |  R3(d, e, a, b, c, 52); |
| 8571 |  R3(c, d, e, a, b, 53); |
| 8572 |  R3(b, c, d, e, a, 54); |
| 8573 |  R3(a, b, c, d, e, 55); |
| 8574 |  R3(e, a, b, c, d, 56); |
| 8575 |  R3(d, e, a, b, c, 57); |
| 8576 |  R3(c, d, e, a, b, 58); |
| 8577 |  R3(b, c, d, e, a, 59); |
| 8578 |  R4(a, b, c, d, e, 60); |
| 8579 |  R4(e, a, b, c, d, 61); |
| 8580 |  R4(d, e, a, b, c, 62); |
| 8581 |  R4(c, d, e, a, b, 63); |
| 8582 |  R4(b, c, d, e, a, 64); |
| 8583 |  R4(a, b, c, d, e, 65); |
| 8584 |  R4(e, a, b, c, d, 66); |
| 8585 |  R4(d, e, a, b, c, 67); |
| 8586 |  R4(c, d, e, a, b, 68); |
| 8587 |  R4(b, c, d, e, a, 69); |
| 8588 |  R4(a, b, c, d, e, 70); |
| 8589 |  R4(e, a, b, c, d, 71); |
| 8590 |  R4(d, e, a, b, c, 72); |
| 8591 |  R4(c, d, e, a, b, 73); |
| 8592 |  R4(b, c, d, e, a, 74); |
| 8593 |  R4(a, b, c, d, e, 75); |
| 8594 |  R4(e, a, b, c, d, 76); |
| 8595 |  R4(d, e, a, b, c, 77); |
| 8596 |  R4(c, d, e, a, b, 78); |
| 8597 |  R4(b, c, d, e, a, 79); |
| 8598 |  state[0] += a; |
| 8599 |  state[1] += b; |
| 8600 |  state[2] += c; |
| 8601 |  state[3] += d; |
| 8602 |  state[4] += e; |
| 8603 |  a = b = c = d = e = 0; |
| 8604 |  memset(block, '\0', sizeof(block)); |
| 8605 | } |
| 8606 | |
| 8607 | |
| 8608 | static void |
| 8609 | SHA1Init(SHA1_CTX *context) |
| 8610 | { |
| 8611 |  context->state[0] = 0x67452301; |
| 8612 |  context->state[1] = 0xEFCDAB89; |
| 8613 |  context->state[2] = 0x98BADCFE; |
| 8614 |  context->state[3] = 0x10325476; |
| 8615 |  context->state[4] = 0xC3D2E1F0; |
| 8616 |  context->count[0] = context->count[1] = 0; |
| 8617 | } |
| 8618 | |
| 8619 | |
| 8620 | static void |
| 8621 | SHA1Update(SHA1_CTX *context, const unsigned char *data, uint32_t len) |
| 8622 | { |
| 8623 |  uint32_t i, j; |
| 8624 | |
| 8625 |  j = context->count[0]; |
| 8626 |  if ((context->count[0] += len << 3) < j) { |
| 8627 |    context->count[1]++; |
| 8628 |  } |
| 8629 |  context->count[1] += (len >> 29); |
| 8630 |  j = (j >> 3) & 63; |
| 8631 |  if ((j + len) > 63) { |
| 8632 |    memcpy(&context->buffer[j], data, (i = 64 - j)); |
| 8633 |    SHA1Transform(context->state, context->buffer); |
| 8634 |    for (; i + 63 < len; i += 64) { |
| 8635 |      SHA1Transform(context->state, &data[i]); |
| 8636 |    } |
| 8637 |    j = 0; |
| 8638 |  } else |
| 8639 |    i = 0; |
| 8640 |  memcpy(&context->buffer[j], &data[i], len - i); |
| 8641 | } |
| 8642 | |
| 8643 | |
| 8644 | static void |
| 8645 | SHA1Final(unsigned char digest[20], SHA1_CTX *context) |
| 8646 | { |
| 8647 |  unsigned i; |
| 8648 |  unsigned char finalcount[8], c; |
| 8649 | |
| 8650 |  for (i = 0; i < 8; i++) { |
| 8651 |    finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] |
| 8652 |     >> ((3 - (i & 3)) * 8)) & 255); |
| 8653 |  } |
| 8654 |  c = 0200; |
| 8655 |  SHA1Update(context, &c, 1); |
| 8656 |  while ((context->count[0] & 504) != 448) { |
| 8657 |    c = 0000; |
| 8658 |    SHA1Update(context, &c, 1); |
| 8659 |  } |
| 8660 |  SHA1Update(context, finalcount, 8); |
| 8661 |  for (i = 0; i < 20; i++) { |
| 8662 |    digest[i] = (unsigned char)((context->state[i >> 2] |
| 8663 |     >> ((3 - (i & 3)) * 8)) & 255); |
| 8664 |  } |
| 8665 |  memset(context, '\0', sizeof(*context)); |
| 8666 |  memset(&finalcount, '\0', sizeof(finalcount)); |
| 8667 | } |
| 8668 | /* END OF SHA1 CODE */ |
| 8669 | |
| 8670 | |
| 8671 | static int |
| 8672 | send_websocket_handshake(struct mg_connection *conn, const char *websock_key) |
| 8673 | { |
| 8674 |  static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
| 8675 |  const char *protocol = NULL; |
| 8676 |  char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; |
| 8677 |  SHA1_CTX sha_ctx; |
| 8678 |  int truncated; |
| 8679 | |
| 8680 |  /* Calculate Sec-WebSocket-Accept reply from Sec-WebSocket-Key. */ |
| 8681 |  mg_snprintf(conn, &truncated, buf, sizeof(buf), "%s%s", websock_key, magic); |
| 8682 |  if (truncated) { |
| 8683 |    conn->must_close = 1; |
| 8684 |    return 0; |
| 8685 |  } |
| 8686 | |
| 8687 |  SHA1Init(&sha_ctx); |
| 8688 |  SHA1Update(&sha_ctx, (unsigned char *)buf, (uint32_t)strlen(buf)); |
| 8689 |  SHA1Final((unsigned char *)sha, &sha_ctx); |
| 8690 |  base64_encode((unsigned char *)sha, sizeof(sha), b64_sha); |
| 8691 |  mg_printf(conn, |
| 8692 |   "HTTP/1.1 101 Switching Protocols\r\n" |
| 8693 |   "Upgrade: websocket\r\n" |
| 8694 |   "Connection: Upgrade\r\n" |
| 8695 |   "Sec-WebSocket-Accept: %s\r\n", |
| 8696 |   b64_sha); |
| 8697 |  protocol = mg_get_header(conn, "Sec-WebSocket-Protocol"); |
| 8698 |  if (protocol) { |
| 8699 |    /* The protocol is a comma seperated list of names. */ |
| 8700 |    /* The server must only return one value from this list. */ |
| 8701 |    /* First check if it is a list or just a single value. */ |
| 8702 |    const char *sep = strchr(protocol, ','); |
| 8703 |    if (sep == NULL) { |
| 8704 |      /* Just a single protocol -> accept it. */ |
| 8705 |      mg_printf(conn, "Sec-WebSocket-Protocol: %s\r\n\r\n", protocol); |
| 8706 |    } else { |
| 8707 |      /* Multiple protocols -> accept the first one. */ |
| 8708 |      /* This is just a quick fix if the client offers multiple |
| 8709 |       * protocols. In order to get the behavior intended by |
| 8710 |       * RFC 6455 (https://tools.ietf.org/rfc/rfc6455.txt), it is |
| 8711 |       * required to have a list of websocket subprotocols accepted |
| 8712 |       * by the server. Then the server must either select a subprotocol |
| 8713 |       * supported by client and server, or the server has to abort the |
| 8714 |       * handshake by not returning a Sec-Websocket-Protocol header if |
| 8715 |       * no subprotocol is acceptable. |
| 8716 |       */ |
| 8717 |      mg_printf(conn, |
| 8718 |       "Sec-WebSocket-Protocol: %.*s\r\n\r\n", |
| 8719 |       (int)(sep - protocol), |
| 8720 |       protocol); |
| 8721 |    } |
| 8722 |    /* TODO: Real subprotocol negotiation instead of just taking the first |
| 8723 |     * websocket subprotocol suggested by the client. */ |
| 8724 |  } else { |
| 8725 |    mg_printf(conn, "%s", "\r\n"); |
| 8726 |  } |
| 8727 | |
| 8728 |  return 1; |
| 8729 | } |
| 8730 | |
| 8731 | |
| 8732 | static void |
| 8733 | read_websocket(struct mg_connection *conn, |
| 8734 | mg_websocket_data_handler ws_data_handler, |
| 8735 | void *callback_data) |
| 8736 | { |
| 8737 |  /* Pointer to the beginning of the portion of the incoming websocket |
| 8738 |   * message queue. |
| 8739 |   * The original websocket upgrade request is never removed, so the queue |
| 8740 |   * begins after it. */ |
| 8741 |  unsigned char *buf = (unsigned char *)conn->buf + conn->request_len; |
| 8742 |  int n, error, exit_by_callback; |
| 8743 | |
| 8744 |  /* body_len is the length of the entire queue in bytes |
| 8745 |   * len is the length of the current message |
| 8746 |   * data_len is the length of the current message's data payload |
| 8747 |   * header_len is the length of the current message's header */ |
| 8748 |  size_t i, len, mask_len = 0, data_len = 0, header_len, body_len; |
| 8749 | |
| 8750 |  /* "The masking key is a 32-bit value chosen at random by the client." |
| 8751 |   * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5 |
| 8752 |  */ |
| 8753 |  unsigned char mask[4]; |
| 8754 | |
| 8755 |  /* data points to the place where the message is stored when passed to |
| 8756 |   * the |
| 8757 |   * websocket_data callback. This is either mem on the stack, or a |
| 8758 |   * dynamically allocated buffer if it is too large. */ |
| 8759 |  char mem[4096]; |
| 8760 |  char *data = mem; |
| 8761 |  unsigned char mop; /* mask flag and opcode */ |
| 8762 |  double timeout = -1.0; |
| 8763 | |
| 8764 |  if (conn->ctx->config[WEBSOCKET_TIMEOUT]) { |
| 8765 |    timeout = atoi(conn->ctx->config[WEBSOCKET_TIMEOUT]) / 1000.0; |
| 8766 |  } |
| 8767 |  if ( ) { |
| 8768 |    timeout = atoi(conn->ctx->config[REQUEST_TIMEOUT]) / 1000.0; |
| 8769 |  } |
| 8770 | |
| 8771 |  mg_set_thread_name("wsock"); |
| 8772 | |
| 8773 |  /* Loop continuously, reading messages from the socket, invoking the |
| 8774 |   * callback, and waiting repeatedly until an error occurs. */ |
| 8775 |  while (!conn->ctx->stop_flag) { |
| 8776 |    header_len = 0; |
| 8777 |    assert(conn->data_len >= conn->request_len); |
| 8778 |    if ((body_len = (size_t)(conn->data_len - conn->request_len)) >= 2) { |
| 8779 |      len = buf[1] & 127; |
| 8780 |      mask_len = buf[1] & 128 ? 4 : 0; |
| 8781 |      if (len < 126 && body_len >= mask_len) { |
| 8782 |        data_len = len; |
| 8783 |        header_len = 2 + mask_len; |
| 8784 |      } else if (len == 126 && body_len >= 4 + mask_len) { |
| 8785 |        header_len = 4 + mask_len; |
| 8786 |        data_len = ((((size_t)buf[2]) << 8) + buf[3]); |
| 8787 |      } else if (body_len >= 10 + mask_len) { |
| 8788 |        header_len = 10 + mask_len; |
| 8789 |        data_len = (((uint64_t)ntohl(*(uint32_t *)(void *)&buf[2])) |
| 8790 |         << 32) + ntohl(*(uint32_t *)(void *)&buf[6]); |
| 8791 |      } |
| 8792 |    } |
| 8793 | |
| 8794 |    if (header_len > 0 && body_len >= header_len) { |
| 8795 |      /* Allocate space to hold websocket payload */ |
| 8796 |      data = mem; |
| 8797 |      if (data_len > sizeof(mem)) { |
| 8798 |        data = (char *)mg_malloc(data_len); |
| 8799 |        if (data == NULL) { |
| 8800 |          /* Allocation failed, exit the loop and then close the |
| 8801 |           * connection */ |
| 8802 |          mg_cry(conn, "websocket out of memory; closing connection"); |
| 8803 |          break; |
| 8804 |        } |
| 8805 |      } |
| 8806 | |
| 8807 |      /* Copy the mask before we shift the queue and destroy it */ |
| 8808 |      if (mask_len > 0) { |
| 8809 |        memcpy(mask, buf + header_len - mask_len, sizeof(mask)); |
| 8810 |      } else { |
| 8811 |        memset(mask, 0, sizeof(mask)); |
| 8812 |      } |
| 8813 | |
| 8814 |      /* Read frame payload from the first message in the queue into |
| 8815 |       * data and advance the queue by moving the memory in place. */ |
| 8816 |      assert(body_len >= header_len); |
| 8817 |      if (data_len + header_len > body_len) { |
| 8818 |        mop = buf[0]; /* current mask and opcode */ |
| 8819 |        /* Overflow case */ |
| 8820 |        len = body_len - header_len; |
| 8821 |        memcpy(data, buf + header_len, len); |
| 8822 |        error = 0; |
| 8823 |        while (len < data_len) { |
| 8824 |          n = pull( |
| 8825 |           NULL, conn, data + len, (int)(data_len - len), timeout); |
| 8826 |          if (n <= 0) { |
| 8827 |            error = 1; |
| 8828 |            break; |
| 8829 |          } |
| 8830 |          len += (size_t)n; |
| 8831 |        } |
| 8832 |        if (error) { |
| 8833 |          mg_cry(conn, "Websocket pull failed; closing connection"); |
| 8834 |          break; |
| 8835 |        } |
| 8836 |        conn->data_len = conn->request_len; |
| 8837 |      } else { |
| 8838 |        mop = buf[0]; /* current mask and opcode, overwritten by |
| 8839 |         * memmove() */ |
| 8840 |        /* Length of the message being read at the front of the |
| 8841 |         * queue */ |
| 8842 |        len = data_len + header_len; |
| 8843 | |
| 8844 |        /* Copy the data payload into the data pointer for the |
| 8845 |         * callback */ |
| 8846 |        memcpy(data, buf + header_len, data_len); |
| 8847 | |
| 8848 |        /* Move the queue forward len bytes */ |
| 8849 |        memmove(buf, buf + len, body_len - len); |
| 8850 | |
| 8851 |        /* Mark the queue as advanced */ |
| 8852 |        conn->data_len -= (int)len; |
| 8853 |      } |
| 8854 | |
| 8855 |      /* Apply mask if necessary */ |
| 8856 |      if (mask_len > 0) { |
| 8857 |        for (i = 0; i < data_len; ++i) { |
| 8858 |          data[i] ^= mask[i & 3]; |
| 8859 |        } |
| 8860 |      } |
| 8861 | |
| 8862 |      /* Exit the loop if callback signals to exit (server side), |
| 8863 |       * or "connection close" opcode received (client side). */ |
| 8864 |      exit_by_callback = 0; |
| 8865 |      if ((ws_data_handler != NULL) |
| 8866 |       && !ws_data_handler(conn, mop, data, data_len, callback_data)) { |
| 8867 |        exit_by_callback = 1; |
| 8868 |      } |
| 8869 | |
| 8870 |      if (data != mem) { |
| 8871 |        mg_free(data); |
| 8872 |      } |
| 8873 | |
| 8874 |      if (exit_by_callback |
| 8875 |       || ((mop & 0xf) == WEBSOCKET_OPCODE_CONNECTION_CLOSE)) { |
| 8876 |        /* Opcode == 8, connection close */ |
| 8877 |        break; |
| 8878 |      } |
| 8879 | |
| 8880 |      /* Not breaking the loop, process next websocket frame. */ |
| 8881 |    } else { |
| 8882 |      /* Read from the socket into the next available location in the |
| 8883 |       * message queue. */ |
| 8884 |      if ((n = pull(NULL, |
| 8885 |       conn, |
| 8886 |       conn->buf + conn->data_len, |
| 8887 |       conn->buf_size - conn->data_len, |
| 8888 |       timeout)) <= 0) { |
| 8889 |        /* Error, no bytes read */ |
| 8890 |        break; |
| 8891 |      } |
| 8892 |      conn->data_len += n; |
| 8893 |    } |
| 8894 |  } |
| 8895 | |
| 8896 |  mg_set_thread_name("worker"); |
| 8897 | } |
| 8898 | |
| 8899 | |
| 8900 | static int |
| 8901 | mg_websocket_write_exec(struct mg_connection *conn, |
| 8902 | int opcode, |
| 8903 | const char *data, |
| 8904 | size_t dataLen, |
| 8905 | uint32_t masking_key) |
| 8906 | { |
| 8907 |  unsigned char header[14]; |
| 8908 |  size_t headerLen = 1; |
| 8909 | |
| 8910 |  int retval = -1; |
| 8911 | |
| 8912 |  header[0] = 0x80 + (opcode & 0xF); |
| 8913 | |
| 8914 |  /* Frame format: http://tools.ietf.org/html/rfc6455#section-5.2 */ |
| 8915 |  if (dataLen < 126) { |
| 8916 |    /* inline 7-bit length field */ |
| 8917 |    header[1] = (unsigned char)dataLen; |
| 8918 |    headerLen = 2; |
| 8919 |  } else if (dataLen <= 0xFFFF) { |
| 8920 |    /* 16-bit length field */ |
| 8921 |    header[1] = 126; |
| 8922 |    *(uint16_t *)(void *)(header + 2) = htons((uint16_t)dataLen); |
| 8923 |    headerLen = 4; |
| 8924 |  } else { |
| 8925 |    /* 64-bit length field */ |
| 8926 |    header[1] = 127; |
| 8927 |    *(uint32_t *)(void *)(header + 2) = htonl((uint64_t)dataLen >> 32); |
| 8928 |    *(uint32_t *)(void *)(header + 6) = htonl(dataLen & 0xFFFFFFFF); |
| 8929 |    headerLen = 10; |
| 8930 |  } |
| 8931 | |
| 8932 |  if (masking_key) { |
| 8933 |    /* add mask */ |
| 8934 |    header[1] |= 0x80; |
| 8935 |    *(uint32_t *)(void *)(header + headerLen) = masking_key; |
| 8936 |    headerLen += 4; |
| 8937 |  } |
| 8938 | |
| 8939 | |
| 8940 |  /* Note that POSIX/Winsock's send() is threadsafe |
| 8941 |   * http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid |
| 8942 |   * but mongoose's mg_printf/mg_write is not (because of the loop in |
| 8943 |   * push(), although that is only a problem if the packet is large or |
| 8944 |   * outgoing buffer is full). */ |
| 8945 |  (void)mg_lock_connection(conn); |
| 8946 |  retval = mg_write(conn, header, headerLen); |
| 8947 |  if (dataLen > 0) { |
| 8948 |    retval = mg_write(conn, data, dataLen); |
| 8949 |  } |
| 8950 |  mg_unlock_connection(conn); |
| 8951 | |
| 8952 |  return retval; |
| 8953 | } |
| 8954 | |
| 8955 | int |
| 8956 | mg_websocket_write(struct mg_connection *conn, |
| 8957 | int opcode, |
| 8958 | const char *data, |
| 8959 | size_t dataLen) |
| 8960 | { |
| 8961 |  return mg_websocket_write_exec(conn, opcode, data, dataLen, 0); |
| 8962 | } |
| 8963 | |
| 8964 | |
| 8965 | static void |
| 8966 | mask_data(const char *in, size_t in_len, uint32_t masking_key, char *out) |
| 8967 | { |
| 8968 |  size_t i = 0; |
| 8969 | |
| 8970 |  i = 0; |
| 8971 |  if ((in_len > 3) && ((ptrdiff_t)in % 4) == 0) { |
| 8972 |    /* Convert in 32 bit words, if data is 4 byte aligned */ |
| 8973 |    while (i < (in_len - 3)) { |
| 8974 |      *(uint32_t *)(void *)(out + i) = |
| 8975 |       *(uint32_t *)(void *)(in + i) ^ masking_key; |
| 8976 |      i += 4; |
| 8977 |    } |
| 8978 |  } |
| 8979 |  if (i != in_len) { |
| 8980 |    /* convert 1-3 remaining bytes if ((dataLen % 4) != 0)*/ |
| 8981 |    while (i < in_len) { |
| 8982 |      *(uint8_t *)(void *)(out + i) = |
| 8983 |       *(uint8_t *)(void *)(in + i) |
| 8984 |       ^ *(((uint8_t *)&masking_key) + (i % 4)); |
| 8985 |      i++; |
| 8986 |    } |
| 8987 |  } |
| 8988 | } |
| 8989 | |
| 8990 | |
| 8991 | int |
| 8992 | mg_websocket_client_write(struct mg_connection *conn, |
| 8993 | int opcode, |
| 8994 | const char *data, |
| 8995 | size_t dataLen) |
| 8996 | { |
| 8997 |  int retval = -1; |
| 8998 |  char *masked_data = (char *)mg_malloc(((dataLen + 7) / 4) * 4); |
| 8999 |  uint32_t masking_key = (uint32_t)get_random(); |
| 9000 | |
| 9001 |  if (masked_data == NULL) { |
| 9002 |    /* Return -1 in an error case */ |
| 9003 |    mg_cry(conn, |
| 9004 |     "Cannot allocate buffer for masked websocket response: " |
| 9005 |     "Out of memory"); |
| 9006 |    return -1; |
| 9007 |  } |
| 9008 | |
| 9009 |  mask_data(data, dataLen, masking_key, masked_data); |
| 9010 | |
| 9011 |  retval = mg_websocket_write_exec( |
| 9012 |   conn, opcode, masked_data, dataLen, masking_key); |
| 9013 |  mg_free(masked_data); |
| 9014 | |
| 9015 |  return retval; |
| 9016 | } |
| 9017 | |
| 9018 | |
| 9019 | static void |
| 9020 | handle_websocket_request(struct mg_connection *conn, |
| 9021 | const char *path, |
| 9022 | int is_callback_resource, |
| 9023 | mg_websocket_connect_handler ws_connect_handler, |
| 9024 | mg_websocket_ready_handler ws_ready_handler, |
| 9025 | mg_websocket_data_handler ws_data_handler, |
| 9026 | mg_websocket_close_handler ws_close_handler, |
| 9027 | void *cbData) |
| 9028 | { |
| 9029 |  const char *websock_key = mg_get_header(conn, "Sec-WebSocket-Key"); |
| 9030 |  const char *version = mg_get_header(conn, "Sec-WebSocket-Version"); |
| 9031 |  int lua_websock = 0; |
| 9032 | |
| 9033 | #if !defined(USE_LUA) |
| 9034 |  (void)path; |
| 9035 | #endif |
| 9036 | |
| 9037 |  /* Step 1: Check websocket protocol version. */ |
| 9038 |  /* Step 1.1: Check Sec-WebSocket-Key. */ |
| 9039 |  if (!websock_key) { |
| 9040 |    /* The RFC standard version (https://tools.ietf.org/html/rfc6455) |
| 9041 |     * requires a Sec-WebSocket-Key header. |
| 9042 |     */ |
| 9043 |    /* It could be the hixie draft version |
| 9044 |     * (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76). |
| 9045 |     */ |
| 9046 |    const char *key1 = mg_get_header(conn, "Sec-WebSocket-Key1"); |
| 9047 |    const char *key2 = mg_get_header(conn, "Sec-WebSocket-Key2"); |
| 9048 |    char key3[8]; |
| 9049 | |
| 9050 |    if ((key1 != NULL) && (key2 != NULL)) { |
| 9051 |      /* This version uses 8 byte body data in a GET request */ |
| 9052 |      conn->content_len = 8; |
| 9053 |      if (8 == mg_read(conn, key3, 8)) { |
| 9054 |        /* This is the hixie version */ |
| 9055 |        send_http_error(conn, |
| 9056 |         426, |
| 9057 |         "%s", |
| 9058 |         "Protocol upgrade to RFC 6455 required"); |
| 9059 |        return; |
| 9060 |      } |
| 9061 |    } |
| 9062 |    /* This is an unknown version */ |
| 9063 |    send_http_error(conn, 400, "%s", "Malformed websocket request"); |
| 9064 |    return; |
| 9065 |  } |
| 9066 | |
| 9067 |  /* Step 1.2: Check websocket protocol version. */ |
| 9068 |  /* The RFC version (https://tools.ietf.org/html/rfc6455) is 13. */ |
| 9069 |  if (version == NULL || strcmp(version, "13") != 0) { |
| 9070 |    /* Reject wrong versions */ |
| 9071 |    send_http_error(conn, 426, "%s", "Protocol upgrade required"); |
| 9072 |    return; |
| 9073 |  } |
| 9074 | |
| 9075 |  /* Step 1.3: Could check for "Host", but we do not really nead this |
| 9076 |   * value for anything, so just ignore it. */ |
| 9077 | |
| 9078 |  /* Step 2: If a callback is responsible, call it. */ |
| 9079 |  if (is_callback_resource) { |
| 9080 |    if (ws_connect_handler != NULL |
| 9081 |     && ws_connect_handler(conn, cbData) != 0) { |
| 9082 |      /* C callback has returned non-zero, do not proceed with |
| 9083 |       * handshake. |
| 9084 |       */ |
| 9085 |      /* Note that C callbacks are no longer called when Lua is |
| 9086 |       * responsible, so C can no longer filter callbacks for Lua. */ |
| 9087 |      return; |
| 9088 |    } |
| 9089 |  } |
| 9090 | #if defined(USE_LUA) |
| 9091 |  /* Step 3: No callback. Check if Lua is responsible. */ |
| 9092 |  else { |
| 9093 |    /* Step 3.1: Check if Lua is responsible. */ |
| 9094 |    if (conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]) { |
| 9095 |      lua_websock = |
| 9096 |       match_prefix(conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS], |
| 9097 |       strlen( |
| 9098 |       conn->ctx->config[LUA_WEBSOCKET_EXTENSIONS]), |
| 9099 |       path); |
| 9100 |    } |
| 9101 | |
| 9102 |    if (lua_websock) { |
| 9103 |      /* Step 3.2: Lua is responsible: call it. */ |
| 9104 |      conn->lua_websocket_state = lua_websocket_new(path, conn); |
| 9105 |      if (!conn->lua_websocket_state) { |
| 9106 |        /* Lua rejected the new client */ |
| 9107 |        return; |
| 9108 |      } |
| 9109 |    } |
| 9110 |  } |
| 9111 | #endif |
| 9112 | |
| 9113 |  /* Step 4: Check if there is a responsible websocket handler. */ |
| 9114 |  if (!is_callback_resource && !lua_websock) { |
| 9115 |    /* There is no callback, an Lua is not responsible either. */ |
| 9116 |    /* Reply with a 404 Not Found or with nothing at all? |
| 9117 |     * TODO (mid): check the websocket standards, how to reply to |
| 9118 |     * requests to invalid websocket addresses. */ |
| 9119 |    send_http_error(conn, 404, "%s", "Not found"); |
| 9120 |    return; |
| 9121 |  } |
| 9122 | |
| 9123 |  /* Step 5: The websocket connection has been accepted */ |
| 9124 |  if (!send_websocket_handshake(conn, websock_key)) { |
| 9125 |    send_http_error(conn, 500, "%s", "Websocket handshake failed"); |
| 9126 |    return; |
| 9127 |  } |
| 9128 | |
| 9129 |  /* Step 6: Call the ready handler */ |
| 9130 |  if (is_callback_resource) { |
| 9131 |    if (ws_ready_handler != NULL) { |
| 9132 |      ws_ready_handler(conn, cbData); |
| 9133 |    } |
| 9134 | #if defined(USE_LUA) |
| 9135 |  } else if (lua_websock) { |
| 9136 |    if (!lua_websocket_ready(conn, conn->lua_websocket_state)) { |
| 9137 |      /* the ready handler returned false */ |
| 9138 |      return; |
| 9139 |    } |
| 9140 | #endif |
| 9141 |  } |
| 9142 | |
| 9143 |  /* Step 7: Enter the read loop */ |
| 9144 |  if (is_callback_resource) { |
| 9145 |    read_websocket(conn, ws_data_handler, cbData); |
| 9146 | #if defined(USE_LUA) |
| 9147 |  } else if (lua_websock) { |
| 9148 |    read_websocket(conn, lua_websocket_data, conn->lua_websocket_state); |
| 9149 | #endif |
| 9150 |  } |
| 9151 | |
| 9152 |  /* Step 8: Call the close handler */ |
| 9153 |  if (ws_close_handler) { |
| 9154 |    ws_close_handler(conn, cbData); |
| 9155 |  } |
| 9156 | } |
| 9157 | |
| 9158 | |
| 9159 | static int |
| 9160 | is_websocket_protocol(const struct mg_connection *conn) |
| 9161 | { |
| 9162 |  const char *upgrade, *connection; |
| 9163 | |
| 9164 |  /* A websocket protocoll has the following HTTP headers: |
| 9165 |   * |
| 9166 |   * Connection: Upgrade |
| 9167 |   * Upgrade: Websocket |
| 9168 |   */ |
| 9169 | |
| 9170 |  upgrade = mg_get_header(conn, "Upgrade"); |
| 9171 |  if (upgrade == NULL) { |
| 9172 |    return 0; /* fail early, don't waste time checking other header |
| 9173 |     * fields |
| 9174 |     */ |
| 9175 |  } |
| 9176 |  if (!mg_strcasestr(upgrade, "websocket")) { |
| 9177 |    return 0; |
| 9178 |  } |
| 9179 | |
| 9180 |  connection = mg_get_header(conn, "Connection"); |
| 9181 |  if (connection == NULL) { |
| 9182 |    return 0; |
| 9183 |  } |
| 9184 |  if (!mg_strcasestr(connection, "upgrade")) { |
| 9185 |    return 0; |
| 9186 |  } |
| 9187 | |
| 9188 |  /* The headers "Host", "Sec-WebSocket-Key", "Sec-WebSocket-Protocol" and |
| 9189 |   * "Sec-WebSocket-Version" are also required. |
| 9190 |   * Don't check them here, since even an unsupported websocket protocol |
| 9191 |   * request still IS a websocket request (in contrast to a standard HTTP |
| 9192 |   * request). It will fail later in handle_websocket_request. |
| 9193 |   */ |
| 9194 | |
| 9195 |  return 1; |
| 9196 | } |
| 9197 | #endif /* !USE_WEBSOCKET */ |
| 9198 | |
| 9199 | |
| 9200 | static int |
| 9201 | isbyte(int n) |
| 9202 | { |
| 9203 |  return n >= 0 && n <= 255; |
| 9204 | } |
| 9205 | |
| 9206 | |
| 9207 | static int |
| 9208 | parse_net(const char *spec, uint32_t *net, uint32_t *mask) |
| 9209 | { |
| 9210 |  int n, a, b, c, d, slash = 32, len = 0; |
| 9211 | |
| 9212 |  if ((sscanf(spec, "%d.%d.%d.%d/%d%n", &a, &b, &c, &d, &slash, &n) == 5 |
| 9213 |   || sscanf(spec, "%d.%d.%d.%d%n", &a, &b, &c, &d, &n) == 4) && isbyte(a) |
| 9214 |   && isbyte(b) && isbyte(c) && isbyte(d) && slash >= 0 |
| 9215 |   && slash < 33) { |
| 9216 |    len = n; |
| 9217 |    *net = ((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) |
| 9218 |     | (uint32_t)d; |
| 9219 |    *mask = slash ? 0xffffffffU << (32 - slash) : 0; |
| 9220 |  } |
| 9221 | |
| 9222 |  return len; |
| 9223 | } |
| 9224 | |
| 9225 | |
| 9226 | static int |
| 9227 | set_throttle(const char *spec, uint32_t remote_ip, const char *uri) |
| 9228 | { |
| 9229 |  int throttle = 0; |
| 9230 |  struct vec vec, val; |
| 9231 |  uint32_t net, mask; |
| 9232 |  char mult; |
| 9233 |  double v; |
| 9234 | |
| 9235 |  while ((spec = next_option(spec, &vec, &val)) != NULL) { |
| 9236 |    mult = ','; |
| 9237 |    if (sscanf(val.ptr, "%lf%c", &v, &mult) < 1 || v < 0 |
| 9238 |     || (lowercase(&mult) != 'k' && lowercase(&mult) != 'm' |
| 9239 |     && mult != ',')) { |
| 9240 |      continue; |
| 9241 |    } |
| 9242 |    v *= lowercase(&mult) == 'k' ? 1024 : lowercase(&mult) == 'm' ? 1048576 |
| 9243 |     : 1; |
| 9244 |    if (vec.len == 1 && vec.ptr[0] == '*') { |
| 9245 |      throttle = (int)v; |
| 9246 |    } else if (parse_net(vec.ptr, &net, &mask) > 0) { |
| 9247 |      if ((remote_ip & mask) == net) { |
| 9248 |        throttle = (int)v; |
| 9249 |      } |
| 9250 |    } else if (match_prefix(vec.ptr, vec.len, uri) > 0) { |
| 9251 |      throttle = (int)v; |
| 9252 |    } |
| 9253 |  } |
| 9254 | |
| 9255 |  return throttle; |
| 9256 | } |
| 9257 | |
| 9258 | |
| 9259 | static uint32_t |
| 9260 | get_remote_ip(const struct mg_connection *conn) |
| 9261 | { |
| 9262 |  if (!conn) { |
| 9263 |    return 0; |
| 9264 |  } |
| 9265 |  return ntohl(*(const uint32_t *)&conn->client.rsa.sin.sin_addr); |
| 9266 | } |
| 9267 | |
| 9268 | |
| 9269 | /* The mg_upload function is superseeded by mg_handle_form_request. */ |
| 9270 | #include "handle_form.inl" |
| 9271 | |
| 9272 | |
| 9273 | #if defined(MG_LEGACY_INTERFACE) |
| 9274 | /* Implement the deprecated mg_upload function by calling the new |
| 9275 | * mg_handle_form_request function. While mg_upload could only handle |
| 9276 | * HTML forms sent as POST request in multipart/form-data format |
| 9277 | * containing only file input elements, mg_handle_form_request can |
| 9278 | * handle all form input elements and all standard request methods. */ |
| 9279 | struct mg_upload_user_data { |
| 9280 |  struct mg_connection *conn; |
| 9281 |  const char *destination_dir; |
| 9282 |  int num_uploaded_files; |
| 9283 | }; |
| 9284 | |
| 9285 | |
| 9286 | /* Helper function for deprecated mg_upload. */ |
| 9287 | static int |
| 9288 | mg_upload_field_found(const char *key, |
| 9289 | const char *filename, |
| 9290 | char *path, |
| 9291 | size_t pathlen, |
| 9292 | void *user_data) |
| 9293 | { |
| 9294 |  int truncated = 0; |
| 9295 |  struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data; |
| 9296 |  (void)key; |
| 9297 | |
| 9298 |  if (!filename) { |
| 9299 |    mg_cry(fud->conn, "%s: No filename set", __func__); |
| 9300 |    return FORM_FIELD_STORAGE_ABORT; |
| 9301 |  } |
| 9302 |  mg_snprintf(fud->conn, |
| 9303 |   &truncated, |
| 9304 |   path, |
| 9305 |   pathlen - 1, |
| 9306 |   "%s/%s", |
| 9307 |   fud->destination_dir, |
| 9308 |   filename); |
| 9309 |  if (!truncated) { |
| 9310 |    mg_cry(fud->conn, "%s: File path too long", __func__); |
| 9311 |    return FORM_FIELD_STORAGE_ABORT; |
| 9312 |  } |
| 9313 |  return FORM_FIELD_STORAGE_STORE; |
| 9314 | } |
| 9315 | |
| 9316 | |
| 9317 | /* Helper function for deprecated mg_upload. */ |
| 9318 | static int |
| 9319 | mg_upload_field_get(const char *key, |
| 9320 | const char *value, |
| 9321 | size_t value_size, |
| 9322 | void *user_data) |
| 9323 | { |
| 9324 |  /* Function should never be called */ |
| 9325 |  (void)key; |
| 9326 |  (void)value; |
| 9327 |  (void)value_size; |
| 9328 |  (void)user_data; |
| 9329 | |
| 9330 |  return 0; |
| 9331 | } |
| 9332 | |
| 9333 | |
| 9334 | /* Helper function for deprecated mg_upload. */ |
| 9335 | static int |
| 9336 | mg_upload_field_stored(const char *path, long long file_size, void *user_data) |
| 9337 | { |
| 9338 |  struct mg_upload_user_data *fud = (struct mg_upload_user_data *)user_data; |
| 9339 |  (void)file_size; |
| 9340 | |
| 9341 |  fud->num_uploaded_files++; |
| 9342 |  fud->conn->ctx->callbacks.upload(fud->conn, path); |
| 9343 | |
| 9344 |  return 0; |
| 9345 | } |
| 9346 | |
| 9347 | |
| 9348 | /* Deprecated function mg_upload - use mg_handle_form_request instead. */ |
| 9349 | int |
| 9350 | mg_upload(struct mg_connection *conn, const char *destination_dir) |
| 9351 | { |
| 9352 |  struct mg_upload_user_data fud = {conn, destination_dir, 0}; |
| 9353 |  struct mg_form_data_handler fdh = {mg_upload_field_found, |
| 9354 |   mg_upload_field_get, |
| 9355 |   mg_upload_field_stored, |
| 9356 |   0}; |
| 9357 |  int ret; |
| 9358 | |
| 9359 |  fdh.user_data = (void *)&fud; |
| 9360 |  ret = mg_handle_form_request(conn, &fdh); |
| 9361 | |
| 9362 |  if (ret < 0) { |
| 9363 |    mg_cry(conn, "%s: Error while parsing the request", __func__); |
| 9364 |  } |
| 9365 | |
| 9366 |  return fud.num_uploaded_files; |
| 9367 | } |
| 9368 | #endif |
| 9369 | |
| 9370 | |
| 9371 | static int |
| 9372 | get_first_ssl_listener_index(const struct mg_context *ctx) |
| 9373 | { |
| 9374 |  unsigned int i; |
| 9375 |  int idx = -1; |
| 9376 |  if (ctx) { |
| 9377 |    for (i = 0; idx == -1 && i < ctx->num_listening_sockets; i++) { |
| 9378 |      idx = ctx->listening_sockets[i].is_ssl ? ((int)(i)) : -1; |
| 9379 |    } |
| 9380 |  } |
| 9381 |  return idx; |
| 9382 | } |
| 9383 | |
| 9384 | |
| 9385 | static void |
| 9386 | redirect_to_https_port(struct mg_connection *conn, int ssl_index) |
| 9387 | { |
| 9388 |  char host[1025]; |
| 9389 |  const char *host_header; |
| 9390 |  size_t hostlen; |
| 9391 | |
| 9392 |  host_header = mg_get_header(conn, "Host"); |
| 9393 |  hostlen = sizeof(host); |
| 9394 |  if (host_header != NULL) { |
| 9395 |    char *pos; |
| 9396 | |
| 9397 |    mg_strlcpy(host, host_header, hostlen); |
| 9398 |    host[hostlen - 1] = '\0'; |
| 9399 |    pos = strchr(host, ':'); |
| 9400 |    if (pos != NULL) { |
| 9401 |      *pos = '\0'; |
| 9402 |    } |
| 9403 |  } else { |
| 9404 |    /* Cannot get host from the Host: header. |
| 9405 |     * Fallback to our IP address. */ |
| 9406 |    if (conn) { |
| 9407 |      sockaddr_to_string(host, hostlen, &conn->client.lsa); |
| 9408 |    } |
| 9409 |  } |
| 9410 | |
| 9411 |  /* Send host, port, uri and (if it exists) ?query_string */ |
| 9412 |  if (conn) { |
| 9413 |    mg_printf(conn, |
| 9414 |     "HTTP/1.1 302 Found\r\nLocation: https://%s:%d%s%s%s\r\n\r\n", |
| 9415 |     host, |
| 9416 |     (int)ntohs( |
| 9417 |     conn->ctx->listening_sockets[ssl_index].lsa.sin.sin_port), |
| 9418 |     conn->request_info.local_uri, |
| 9419 |     (conn->request_info.query_string == NULL) ? "" : "?", |
| 9420 |     (conn->request_info.query_string == NULL) |
| 9421 |     ? "" |
| 9422 |     : conn->request_info.query_string); |
| 9423 |  } |
| 9424 | } |
| 9425 | |
| 9426 | |
| 9427 | static void |
| 9428 | mg_set_handler_type(struct mg_context *ctx, |
| 9429 | const char *uri, |
| 9430 | int handler_type, |
| 9431 | int is_delete_request, |
| 9432 | mg_request_handler handler, |
| 9433 | mg_websocket_connect_handler connect_handler, |
| 9434 | mg_websocket_ready_handler ready_handler, |
| 9435 | mg_websocket_data_handler data_handler, |
| 9436 | mg_websocket_close_handler close_handler, |
| 9437 | mg_authorization_handler auth_handler, |
| 9438 | void *cbdata) |
| 9439 | { |
| 9440 |  struct mg_handler_info *tmp_rh, **lastref; |
| 9441 |  size_t urilen = strlen(uri); |
| 9442 | |
| 9443 |  if (handler_type == WEBSOCKET_HANDLER) { |
| 9444 |    /* assert(handler == NULL); */ |
| 9445 |    /* assert(is_delete_request || connect_handler!=NULL || |
| 9446 |     * ready_handler!=NULL || data_handler!=NULL || |
| 9447 |     * close_handler!=NULL); |
| 9448 |     */ |
| 9449 |    /* assert(auth_handler == NULL); */ |
| 9450 |    if (handler != NULL) { |
| 9451 |      return; |
| 9452 |    } |
| 9453 |    if ( |
| 9454 |     |
| 9455 |     |
| 9456 |     ) { |
| 9457 |      return; |
| 9458 |    } |
| 9459 |    if (auth_handler != NULL) { |
| 9460 |      return; |
| 9461 |    } |
| 9462 |  } else if (handler_type == REQUEST_HANDLER) { |
| 9463 |    /* assert(connect_handler==NULL && ready_handler==NULL && |
| 9464 |     * data_handler==NULL && close_handler==NULL); */ |
| 9465 |    /* assert(is_delete_request || (handler!=NULL)); |
| 9466 |     */ |
| 9467 |    /* assert(auth_handler == NULL); */ |
| 9468 |    if (connect_handler != NULL || ready_handler != NULL |
| 9469 |     || data_handler != NULL |
| 9470 |     || close_handler != NULL) { |
| 9471 |      return; |
| 9472 |    } |
| 9473 |    if (!is_delete_request && (handler == NULL)) { |
| 9474 |      return; |
| 9475 |    } |
| 9476 |    if (auth_handler != NULL) { |
| 9477 |      return; |
| 9478 |    } |
| 9479 |  } else { /* AUTH_HANDLER */ |
| 9480 |     /* assert(handler == NULL); */ |
| 9481 |     /* assert(connect_handler==NULL && ready_handler==NULL && |
| 9482 |     * data_handler==NULL && close_handler==NULL); */ |
| 9483 |    /* assert(auth_handler != NULL); */ |
| 9484 |    if (handler != NULL) { |
| 9485 |      return; |
| 9486 |    } |
| 9487 |    if (connect_handler != NULL || ready_handler != NULL |
| 9488 |     || data_handler != NULL |
| 9489 |     || close_handler != NULL) { |
| 9490 |      return; |
| 9491 |    } |
| 9492 |    if (!is_delete_request && (auth_handler == NULL)) { |
| 9493 |      return; |
| 9494 |    } |
| 9495 |  } |
| 9496 | |
| 9497 |  if (!ctx) { |
| 9498 |    return; |
| 9499 |  } |
| 9500 | |
| 9501 |  mg_lock_context(ctx); |
| 9502 | |
| 9503 |  /* first try to find an existing handler */ |
| 9504 |  lastref = &(ctx->handlers); |
| 9505 |  for (tmp_rh = ctx->handlers; tmp_rh != NULL; tmp_rh = tmp_rh->next) { |
| 9506 |    if (tmp_rh->handler_type == handler_type) { |
| 9507 |      if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) { |
| 9508 |        if (!is_delete_request) { |
| 9509 |          /* update existing handler */ |
| 9510 |          if (handler_type == REQUEST_HANDLER) { |
| 9511 |            tmp_rh->handler = handler; |
| 9512 |          } else if (handler_type == WEBSOCKET_HANDLER) { |
| 9513 |            tmp_rh->connect_handler = connect_handler; |
| 9514 |            tmp_rh->ready_handler = ready_handler; |
| 9515 |            tmp_rh->data_handler = data_handler; |
| 9516 |            tmp_rh->close_handler = close_handler; |
| 9517 |          } else { /* AUTH_HANDLER */ |
| 9518 |            tmp_rh->auth_handler = auth_handler; |
| 9519 |          } |
| 9520 |          tmp_rh->cbdata = cbdata; |
| 9521 |        } else { |
| 9522 |          /* remove existing handler */ |
| 9523 |          *lastref = tmp_rh->next; |
| 9524 |          mg_free(tmp_rh->uri); |
| 9525 |          mg_free(tmp_rh); |
| 9526 |        } |
| 9527 |        mg_unlock_context(ctx); |
| 9528 |        return; |
| 9529 |      } |
| 9530 |    } |
| 9531 |    lastref = &(tmp_rh->next); |
| 9532 |  } |
| 9533 | |
| 9534 |  if (is_delete_request) { |
| 9535 |    /* no handler to set, this was a remove request to a non-existing |
| 9536 |     * handler */ |
| 9537 |    mg_unlock_context(ctx); |
| 9538 |    return; |
| 9539 |  } |
| 9540 | |
| 9541 |  tmp_rh = |
| 9542 |   (struct mg_handler_info *)mg_calloc(sizeof(struct mg_handler_info), 1); |
| 9543 |  if (tmp_rh == NULL) { |
| 9544 |    mg_unlock_context(ctx); |
| 9545 |    mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM"); |
| 9546 |    return; |
| 9547 |  } |
| 9548 |  tmp_rh->uri = mg_strdup(uri); |
| 9549 |  if (!tmp_rh->uri) { |
| 9550 |    mg_unlock_context(ctx); |
| 9551 |    mg_free(tmp_rh); |
| 9552 |    mg_cry(fc(ctx), "%s", "Cannot create new request handler struct, OOM"); |
| 9553 |    return; |
| 9554 |  } |
| 9555 |  tmp_rh->uri_len = urilen; |
| 9556 |  if (handler_type == REQUEST_HANDLER) { |
| 9557 |    tmp_rh->handler = handler; |
| 9558 |  } else if (handler_type == WEBSOCKET_HANDLER) { |
| 9559 |    tmp_rh->connect_handler = connect_handler; |
| 9560 |    tmp_rh->ready_handler = ready_handler; |
| 9561 |    tmp_rh->data_handler = data_handler; |
| 9562 |    tmp_rh->close_handler = close_handler; |
| 9563 |  } else { /* AUTH_HANDLER */ |
| 9564 |    tmp_rh->auth_handler = auth_handler; |
| 9565 |  } |
| 9566 |  tmp_rh->cbdata = cbdata; |
| 9567 |  tmp_rh->handler_type = handler_type; |
| 9568 |  tmp_rh->next = NULL; |
| 9569 | |
| 9570 |  *lastref = tmp_rh; |
| 9571 |  mg_unlock_context(ctx); |
| 9572 | } |
| 9573 | |
| 9574 | |
| 9575 | void |
| 9576 | mg_set_request_handler(struct mg_context *ctx, |
| 9577 | const char *uri, |
| 9578 | mg_request_handler handler, |
| 9579 | void *cbdata) |
| 9580 | { |
| 9581 |  mg_set_handler_type(ctx, |
| 9582 |   uri, |
| 9583 |   REQUEST_HANDLER, |
| 9584 |   handler == NULL, |
| 9585 |   handler, |
| 9586 |   NULL, |
| 9587 |   NULL, |
| 9588 |   NULL, |
| 9589 |   NULL, |
| 9590 |   NULL, |
| 9591 |   cbdata); |
| 9592 | } |
| 9593 | |
| 9594 | |
| 9595 | void |
| 9596 | mg_set_websocket_handler(struct mg_context *ctx, |
| 9597 | const char *uri, |
| 9598 | mg_websocket_connect_handler connect_handler, |
| 9599 | mg_websocket_ready_handler ready_handler, |
| 9600 | mg_websocket_data_handler data_handler, |
| 9601 | mg_websocket_close_handler close_handler, |
| 9602 | void *cbdata) |
| 9603 | { |
| 9604 |  int is_delete_request = (connect_handler == NULL) && (ready_handler == NULL) |
| 9605 |   && (data_handler == NULL) |
| 9606 |   && (close_handler == NULL); |
| 9607 |  mg_set_handler_type(ctx, |
| 9608 |   uri, |
| 9609 |   WEBSOCKET_HANDLER, |
| 9610 |   is_delete_request, |
| 9611 |   NULL, |
| 9612 |   connect_handler, |
| 9613 |   ready_handler, |
| 9614 |   data_handler, |
| 9615 |   close_handler, |
| 9616 |   NULL, |
| 9617 |   cbdata); |
| 9618 | } |
| 9619 | |
| 9620 | |
| 9621 | void |
| 9622 | mg_set_auth_handler(struct mg_context *ctx, |
| 9623 | const char *uri, |
| 9624 | mg_request_handler handler, |
| 9625 | void *cbdata) |
| 9626 | { |
| 9627 |  mg_set_handler_type(ctx, |
| 9628 |   uri, |
| 9629 |   AUTH_HANDLER, |
| 9630 |   handler == NULL, |
| 9631 |   NULL, |
| 9632 |   NULL, |
| 9633 |   NULL, |
| 9634 |   NULL, |
| 9635 |   NULL, |
| 9636 |   handler, |
| 9637 |   cbdata); |
| 9638 | } |
| 9639 | |
| 9640 | |
| 9641 | static int |
| 9642 | get_request_handler(struct mg_connection *conn, |
| 9643 | int handler_type, |
| 9644 | mg_request_handler *handler, |
| 9645 | mg_websocket_connect_handler *connect_handler, |
| 9646 | mg_websocket_ready_handler *ready_handler, |
| 9647 | mg_websocket_data_handler *data_handler, |
| 9648 | mg_websocket_close_handler *close_handler, |
| 9649 | mg_authorization_handler *auth_handler, |
| 9650 | void **cbdata) |
| 9651 | { |
| 9652 |  const struct mg_request_info *request_info = mg_get_request_info(conn); |
| 9653 |  if (request_info) { |
| 9654 |    const char *uri = request_info->local_uri; |
| 9655 |    size_t urilen = strlen(uri); |
| 9656 |    struct mg_handler_info *tmp_rh; |
| 9657 | |
| 9658 |    if (!conn || !conn->ctx) { |
| 9659 |      return 0; |
| 9660 |    } |
| 9661 | |
| 9662 |    mg_lock_context(conn->ctx); |
| 9663 | |
| 9664 |    /* first try for an exact match */ |
| 9665 |    for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; |
| 9666 |     tmp_rh = tmp_rh->next) { |
| 9667 |      if (tmp_rh->handler_type == handler_type) { |
| 9668 |        if (urilen == tmp_rh->uri_len && !strcmp(tmp_rh->uri, uri)) { |
| 9669 |          if (handler_type == WEBSOCKET_HANDLER) { |
| 9670 |            *connect_handler = tmp_rh->connect_handler; |
| 9671 |            *ready_handler = tmp_rh->ready_handler; |
| 9672 |            *data_handler = tmp_rh->data_handler; |
| 9673 |            *close_handler = tmp_rh->close_handler; |
| 9674 |          } else if (handler_type == REQUEST_HANDLER) { |
| 9675 |            *handler = tmp_rh->handler; |
| 9676 |          } else { /* AUTH_HANDLER */ |
| 9677 |            *auth_handler = tmp_rh->auth_handler; |
| 9678 |          } |
| 9679 |          *cbdata = tmp_rh->cbdata; |
| 9680 |          mg_unlock_context(conn->ctx); |
| 9681 |          return 1; |
| 9682 |        } |
| 9683 |      } |
| 9684 |    } |
| 9685 | |
| 9686 |    /* next try for a partial match, we will accept uri/something */ |
| 9687 |    for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; |
| 9688 |     tmp_rh = tmp_rh->next) { |
| 9689 |      if (tmp_rh->handler_type == handler_type) { |
| 9690 |        if (tmp_rh->uri_len < urilen && uri[tmp_rh->uri_len] == '/' |
| 9691 |         && memcmp(tmp_rh->uri, uri, tmp_rh->uri_len) == 0) { |
| 9692 |          if (handler_type == WEBSOCKET_HANDLER) { |
| 9693 |            *connect_handler = tmp_rh->connect_handler; |
| 9694 |            *ready_handler = tmp_rh->ready_handler; |
| 9695 |            *data_handler = tmp_rh->data_handler; |
| 9696 |            *close_handler = tmp_rh->close_handler; |
| 9697 |          } else if (handler_type == REQUEST_HANDLER) { |
| 9698 |            *handler = tmp_rh->handler; |
| 9699 |          } else { /* AUTH_HANDLER */ |
| 9700 |            *auth_handler = tmp_rh->auth_handler; |
| 9701 |          } |
| 9702 |          *cbdata = tmp_rh->cbdata; |
| 9703 |          mg_unlock_context(conn->ctx); |
| 9704 |          return 1; |
| 9705 |        } |
| 9706 |      } |
| 9707 |    } |
| 9708 | |
| 9709 |    /* finally try for pattern match */ |
| 9710 |    for (tmp_rh = conn->ctx->handlers; tmp_rh != NULL; |
| 9711 |     tmp_rh = tmp_rh->next) { |
| 9712 |      if (tmp_rh->handler_type == handler_type) { |
| 9713 |        if (match_prefix(tmp_rh->uri, tmp_rh->uri_len, uri) > 0) { |
| 9714 |          if (handler_type == WEBSOCKET_HANDLER) { |
| 9715 |            *connect_handler = tmp_rh->connect_handler; |
| 9716 |            *ready_handler = tmp_rh->ready_handler; |
| 9717 |            *data_handler = tmp_rh->data_handler; |
| 9718 |            *close_handler = tmp_rh->close_handler; |
| 9719 |          } else if (handler_type == REQUEST_HANDLER) { |
| 9720 |            *handler = tmp_rh->handler; |
| 9721 |          } else { /* AUTH_HANDLER */ |
| 9722 |            *auth_handler = tmp_rh->auth_handler; |
| 9723 |          } |
| 9724 |          *cbdata = tmp_rh->cbdata; |
| 9725 |          mg_unlock_context(conn->ctx); |
| 9726 |          return 1; |
| 9727 |        } |
| 9728 |      } |
| 9729 |    } |
| 9730 | |
| 9731 |    mg_unlock_context(conn->ctx); |
| 9732 |  } |
| 9733 |  return 0; /* none found */ |
| 9734 | } |
| 9735 | |
| 9736 | |
| 9737 | #if defined(USE_WEBSOCKET) && defined(MG_LEGACY_INTERFACE) |
| 9738 | static int |
| 9739 | deprecated_websocket_connect_wrapper(const struct mg_connection *conn, |
| 9740 | void *cbdata) |
| 9741 | { |
| 9742 |  struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata; |
| 9743 |  if (pcallbacks->websocket_connect) { |
| 9744 |    return pcallbacks->websocket_connect(conn); |
| 9745 |  } |
| 9746 |  /* No handler set - assume "OK" */ |
| 9747 |  return 0; |
| 9748 | } |
| 9749 | |
| 9750 | |
| 9751 | static void |
| 9752 | deprecated_websocket_ready_wrapper(struct mg_connection *conn, void *cbdata) |
| 9753 | { |
| 9754 |  struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata; |
| 9755 |  if (pcallbacks->websocket_ready) { |
| 9756 |    pcallbacks->websocket_ready(conn); |
| 9757 |  } |
| 9758 | } |
| 9759 | |
| 9760 | |
| 9761 | static int |
| 9762 | deprecated_websocket_data_wrapper(struct mg_connection *conn, |
| 9763 | int bits, |
| 9764 | char *data, |
| 9765 | size_t len, |
| 9766 | void *cbdata) |
| 9767 | { |
| 9768 |  struct mg_callbacks *pcallbacks = (struct mg_callbacks *)cbdata; |
| 9769 |  if (pcallbacks->websocket_data) { |
| 9770 |    return pcallbacks->websocket_data(conn, bits, data, len); |
| 9771 |  } |
| 9772 |  /* No handler set - assume "OK" */ |
| 9773 |  return 1; |
| 9774 | } |
| 9775 | #endif |
| 9776 | |
| 9777 | |
| 9778 | /* This is the heart of the Civetweb's logic. |
| 9779 | * This function is called when the request is read, parsed and validated, |
| 9780 | * and Civetweb must decide what action to take: serve a file, or |
| 9781 | * a directory, or call embedded function, etcetera. */ |
| 9782 | static void |
| 9783 | handle_request(struct mg_connection *conn) |
| 9784 | { |
| 9785 |  if (conn) { |
| 9786 |    struct mg_request_info *ri = &conn->request_info; |
| 9787 |    char path[PATH_MAX]; |
| 9788 |    int uri_len, ssl_index; |
| 9789 |    int is_found = 0, is_script_resource = 0, is_websocket_request = 0, |
| 9790 |     is_put_or_delete_request = 0, is_callback_resource = 0; |
| 9791 |    int i; |
| 9792 |    struct file file = STRUCT_FILE_INITIALIZER; |
| 9793 |    mg_request_handler callback_handler = NULL; |
| 9794 |    mg_websocket_connect_handler ws_connect_handler = NULL; |
| 9795 |    mg_websocket_ready_handler ws_ready_handler = NULL; |
| 9796 |    mg_websocket_data_handler ws_data_handler = NULL; |
| 9797 |    mg_websocket_close_handler ws_close_handler = NULL; |
| 9798 |    void *callback_data = NULL; |
| 9799 |    mg_authorization_handler auth_handler = NULL; |
| 9800 |    void *auth_callback_data = NULL; |
| 9801 | #if !defined(NO_FILES) |
| 9802 |    time_t curtime = time(NULL); |
| 9803 |    char date[64]; |
| 9804 | #endif |
| 9805 | |
| 9806 |    path[0] = 0; |
| 9807 | |
| 9808 |    if (!ri) { |
| 9809 |      return; |
| 9810 |    } |
| 9811 | |
| 9812 |    /* 1. get the request url */ |
| 9813 |    /* 1.1. split into url and query string */ |
| 9814 |    if ((conn->request_info.query_string = strchr(ri->request_uri, '?')) |
| 9815 |     != NULL) { |
| 9816 |      *((char *)conn->request_info.query_string++) = '\0'; |
| 9817 |    } |
| 9818 |    uri_len = (int)strlen(ri->local_uri); |
| 9819 | |
| 9820 |    /* 1.2. decode url (if config says so) */ |
| 9821 |    if (should_decode_url(conn)) { |
| 9822 |      mg_url_decode( |
| 9823 |       ri->local_uri, uri_len, (char *)ri->local_uri, uri_len + 1, 0); |
| 9824 |    } |
| 9825 | |
| 9826 |    /* 1.3. clean URIs, so a path like allowed_dir/../forbidden_file is |
| 9827 |     * not possible */ |
| 9828 |    remove_double_dots_and_double_slashes((char *)ri->local_uri); |
| 9829 | |
| 9830 |    /* step 1. completed, the url is known now */ |
| 9831 |    DEBUG_TRACE("URL: %s", ri->local_uri); |
| 9832 | |
| 9833 |    /* 2. do a https redirect, if required */ |
| 9834 |    if (!conn->client.is_ssl && conn->client.ssl_redir) { |
| 9835 |      ssl_index = get_first_ssl_listener_index(conn->ctx); |
| 9836 |      if (ssl_index >= 0) { |
| 9837 |        redirect_to_https_port(conn, ssl_index); |
| 9838 |      } else { |
| 9839 |        /* A http to https forward port has been specified, |
| 9840 |         * but no https port to forward to. */ |
| 9841 |        send_http_error(conn, |
| 9842 |         503, |
| 9843 |         "%s", |
| 9844 |         "Error: SSL forward not configured properly"); |
| 9845 |        mg_cry(conn, "Can not redirect to SSL, no SSL port available"); |
| 9846 |      } |
| 9847 |      return; |
| 9848 |    } |
| 9849 | |
| 9850 |    /* 3. if this ip has limited speed, set it for this connection */ |
| 9851 |    conn->throttle = set_throttle(conn->ctx->config[THROTTLE], |
| 9852 |     get_remote_ip(conn), |
| 9853 |     ri->local_uri); |
| 9854 | |
| 9855 |    /* 4. call a "handle everything" callback, if registered */ |
| 9856 |    if (conn->ctx->callbacks.begin_request != NULL) { |
| 9857 |      /* Note that since V1.7 the "begin_request" function is called |
| 9858 |       * before an authorization check. If an authorization check is |
| 9859 |       * required, use a request_handler instead. */ |
| 9860 |      i = conn->ctx->callbacks.begin_request(conn); |
| 9861 |      if (i > 0) { |
| 9862 |        /* callback already processed the request. Store the |
| 9863 |         return value as a status code for the access log. */ |
| 9864 |        conn->status_code = i; |
| 9865 |        return; |
| 9866 |      } else if (i == 0) { |
| 9867 |        /* civetweb should process the request */ |
| 9868 |      } else { |
| 9869 |        /* unspecified - may change with the next version */ |
| 9870 |        return; |
| 9871 |      } |
| 9872 |    } |
| 9873 | |
| 9874 |    /* request not yet handled by a handler or redirect, so the request |
| 9875 |     * is processed here */ |
| 9876 | |
| 9877 |    /* 5. interpret the url to find out how the request must be handled |
| 9878 |     */ |
| 9879 |    /* 5.1. first test, if the request targets the regular http(s):// |
| 9880 |     * protocol namespace or the websocket ws(s):// protocol namespace. |
| 9881 |     */ |
| 9882 |    is_websocket_request = is_websocket_protocol(conn); |
| 9883 | |
| 9884 |    /* 5.2. check if the request will be handled by a callback */ |
| 9885 |    if (get_request_handler(conn, |
| 9886 |     is_websocket_request ? WEBSOCKET_HANDLER |
| 9887 |     : REQUEST_HANDLER, |
| 9888 |     &callback_handler, |
| 9889 |     &ws_connect_handler, |
| 9890 |     &ws_ready_handler, |
| 9891 |     &ws_data_handler, |
| 9892 |     &ws_close_handler, |
| 9893 |     NULL, |
| 9894 |     &callback_data)) { |
| 9895 |      /* 5.2.1. A callback will handle this request. All requests |
| 9896 |       * handled |
| 9897 |       * by a callback have to be considered as requests to a script |
| 9898 |       * resource. */ |
| 9899 |      is_callback_resource = 1; |
| 9900 |      is_script_resource = 1; |
| 9901 |      is_put_or_delete_request = is_put_or_delete_method(conn); |
| 9902 |    } else { |
| 9903 |    no_callback_resource: |
| 9904 |      /* 5.2.2. No callback is responsible for this request. The URI |
| 9905 |       * addresses a file based resource (static content or Lua/cgi |
| 9906 |       * scripts in the file system). */ |
| 9907 |      is_callback_resource = 0; |
| 9908 |      interpret_uri(conn, |
| 9909 |       path, |
| 9910 |       sizeof(path), |
| 9911 |       &file, |
| 9912 |       &is_found, |
| 9913 |       &is_script_resource, |
| 9914 |       &is_websocket_request, |
| 9915 |       &is_put_or_delete_request); |
| 9916 |    } |
| 9917 | |
| 9918 |    /* 6. authorization check */ |
| 9919 |    /* 6.1. a custom authorization handler is installed */ |
| 9920 |    if (get_request_handler(conn, |
| 9921 |     AUTH_HANDLER, |
| 9922 |     NULL, |
| 9923 |     NULL, |
| 9924 |     NULL, |
| 9925 |     NULL, |
| 9926 |     NULL, |
| 9927 |     &auth_handler, |
| 9928 |     &auth_callback_data)) { |
| 9929 |      if (!auth_handler(conn, auth_callback_data)) { |
| 9930 |        return; |
| 9931 |      } |
| 9932 |    } else if (is_put_or_delete_request && !is_script_resource |
| 9933 |     && !is_callback_resource) { |
| 9934 | /* 6.2. this request is a PUT/DELETE to a real file */ |
| 9935 | /* 6.2.1. thus, the server must have real files */ |
| 9936 | #if defined(NO_FILES) |
| 9937 |      if (1) { |
| 9938 | #else |
| 9939 |      if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { |
| 9940 | #endif |
| 9941 |        /* This server does not have any real files, thus the |
| 9942 |         * PUT/DELETE methods are not valid. */ |
| 9943 |        send_http_error(conn, |
| 9944 |         405, |
| 9945 |         "%s method not allowed", |
| 9946 |         conn->request_info.request_method); |
| 9947 |        return; |
| 9948 |      } |
| 9949 | |
| 9950 | #if !defined(NO_FILES) |
| 9951 |      /* 6.2.2. Check if put authorization for static files is |
| 9952 |       * available. |
| 9953 |       */ |
| 9954 |      if (!is_authorized_for_put(conn)) { |
| 9955 |        send_authorization_request(conn); |
| 9956 |        return; |
| 9957 |      } |
| 9958 | #endif |
| 9959 | |
| 9960 |    } else { |
| 9961 |      /* 6.3. This is either a OPTIONS, GET, HEAD or POST request, |
| 9962 |       * or it is a PUT or DELETE request to a resource that does not |
| 9963 |       * correspond to a file. Check authorization. */ |
| 9964 |      if (!check_authorization(conn, path)) { |
| 9965 |        send_authorization_request(conn); |
| 9966 |        return; |
| 9967 |      } |
| 9968 |    } |
| 9969 | |
| 9970 |    /* request is authorized or does not need authorization */ |
| 9971 | |
| 9972 |    /* 7. check if there are request handlers for this uri */ |
| 9973 |    if (is_callback_resource) { |
| 9974 |      if (!is_websocket_request) { |
| 9975 |        i = callback_handler(conn, callback_data); |
| 9976 |        if (i > 0) { |
| 9977 |          /* Do nothing, callback has served the request. Store |
| 9978 |           * the |
| 9979 |           * return value as status code for the log and discard |
| 9980 |           * all |
| 9981 |           * data from the client not used by the callback. */ |
| 9982 |          conn->status_code = i; |
| 9983 |          discard_unread_request_data(conn); |
| 9984 |        } else { |
| 9985 |          /* TODO (high): what if the handler did NOT handle the |
| 9986 |           * request */ |
| 9987 |          /* The last version did handle this as a file request, |
| 9988 |           * but |
| 9989 |           * since a file request is not always a script resource, |
| 9990 |           * the authorization check might be different */ |
| 9991 |          interpret_uri(conn, |
| 9992 |           path, |
| 9993 |           sizeof(path), |
| 9994 |           &file, |
| 9995 |           &is_found, |
| 9996 |           &is_script_resource, |
| 9997 |           &is_websocket_request, |
| 9998 |           &is_put_or_delete_request); |
| 9999 |          callback_handler = NULL; |
| 10000 | |
| 10001 |          /* TODO (very low): goto is deprecated but for the |
| 10002 |           * moment, |
| 10003 |           * a goto is simpler than some curious loop. */ |
| 10004 |          /* The situation "callback does not handle the request" |
| 10005 |           * needs to be reconsidered anyway. */ |
| 10006 |          goto no_callback_resource; |
| 10007 |        } |
| 10008 |      } else { |
| 10009 | #if defined(USE_WEBSOCKET) |
| 10010 |        handle_websocket_request(conn, |
| 10011 |         path, |
| 10012 |         is_callback_resource, |
| 10013 |         ws_connect_handler, |
| 10014 |         ws_ready_handler, |
| 10015 |         ws_data_handler, |
| 10016 |         ws_close_handler, |
| 10017 |         callback_data); |
| 10018 | #endif |
| 10019 |      } |
| 10020 |      return; |
| 10021 |    } |
| 10022 | |
| 10023 | /* 8. handle websocket requests */ |
| 10024 | #if defined(USE_WEBSOCKET) |
| 10025 |    if (is_websocket_request) { |
| 10026 |      if (is_script_resource) { |
| 10027 |        /* Websocket Lua script */ |
| 10028 |        handle_websocket_request(conn, |
| 10029 |         path, |
| 10030 |         0 /* Lua Script */, |
| 10031 |         NULL, |
| 10032 |         NULL, |
| 10033 |         NULL, |
| 10034 |         NULL, |
| 10035 |         &conn->ctx->callbacks); |
| 10036 |      } else { |
| 10037 | #if defined(MG_LEGACY_INTERFACE) |
| 10038 |        handle_websocket_request( |
| 10039 |         conn, |
| 10040 |         path, |
| 10041 |         !is_script_resource /* could be deprecated global callback */, |
| 10042 |         deprecated_websocket_connect_wrapper, |
| 10043 |         deprecated_websocket_ready_wrapper, |
| 10044 |         deprecated_websocket_data_wrapper, |
| 10045 |         NULL, |
| 10046 |         &conn->ctx->callbacks); |
| 10047 | #else |
| 10048 |        send_http_error(conn, 404, "%s", "Not found"); |
| 10049 | #endif |
| 10050 |      } |
| 10051 |      return; |
| 10052 |    } else |
| 10053 | #endif |
| 10054 | |
| 10055 | #if defined(NO_FILES) |
| 10056 |      /* 9a. In case the server uses only callbacks, this uri is |
| 10057 |       * unknown. |
| 10058 |       * Then, all request handling ends here. */ |
| 10059 |      send_http_error(conn, 404, "%s", "Not Found"); |
| 10060 | |
| 10061 | #else |
| 10062 |    /* 9b. This request is either for a static file or resource handled |
| 10063 |     * by a script file. Thus, a DOCUMENT_ROOT must exist. */ |
| 10064 |    if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { |
| 10065 |      send_http_error(conn, 404, "%s", "Not Found"); |
| 10066 |      return; |
| 10067 |    } |
| 10068 | |
| 10069 |    /* 10. File is handled by a script. */ |
| 10070 |    if (is_script_resource) { |
| 10071 |      handle_file_based_request(conn, path, &file); |
| 10072 |      return; |
| 10073 |    } |
| 10074 | |
| 10075 |    /* 11. Handle put/delete/mkcol requests */ |
| 10076 |    if (is_put_or_delete_request) { |
| 10077 |      /* 11.1. PUT method */ |
| 10078 |      if (!strcmp(ri->request_method, "PUT")) { |
| 10079 |        put_file(conn, path); |
| 10080 |        return; |
| 10081 |      } |
| 10082 |      /* 11.2. DELETE method */ |
| 10083 |      if (!strcmp(ri->request_method, "DELETE")) { |
| 10084 |        delete_file(conn, path); |
| 10085 |        return; |
| 10086 |      } |
| 10087 |      /* 11.3. MKCOL method */ |
| 10088 |      if (!strcmp(ri->request_method, "MKCOL")) { |
| 10089 |        mkcol(conn, path); |
| 10090 |        return; |
| 10091 |      } |
| 10092 |      /* 11.4. PATCH method |
| 10093 |       * This method is not supported for static resources, |
| 10094 |       * only for scripts (Lua, CGI) and callbacks. */ |
| 10095 |      send_http_error(conn, |
| 10096 |       405, |
| 10097 |       "%s method not allowed", |
| 10098 |       conn->request_info.request_method); |
| 10099 |      return; |
| 10100 |    } |
| 10101 | |
| 10102 |    /* 11. File does not exist, or it was configured that it should be |
| 10103 |     * hidden */ |
| 10104 |    if (!is_found || (must_hide_file(conn, path))) { |
| 10105 |      send_http_error(conn, 404, "%s", "Not found"); |
| 10106 |      return; |
| 10107 |    } |
| 10108 | |
| 10109 |    /* 12. Directory uris should end with a slash */ |
| 10110 |    if (file.is_directory && ri->local_uri[uri_len - 1] != '/') { |
| 10111 |      gmt_time_string(date, sizeof(date), &curtime); |
| 10112 |      mg_printf(conn, |
| 10113 |       "HTTP/1.1 301 Moved Permanently\r\n" |
| 10114 |       "Location: %s/\r\n" |
| 10115 |       "Date: %s\r\n" |
| 10116 |       /* "Cache-Control: private\r\n" (= default) */ |
| 10117 |       "Content-Length: 0\r\n" |
| 10118 |       "Connection: %s\r\n\r\n", |
| 10119 |       ri->request_uri, |
| 10120 |       date, |
| 10121 |       suggest_connection_header(conn)); |
| 10122 |      return; |
| 10123 |    } |
| 10124 | |
| 10125 |    /* 13. Handle other methods than GET/HEAD */ |
| 10126 |    /* 13.1. Handle PROPFIND */ |
| 10127 |    if (!strcmp(ri->request_method, "PROPFIND")) { |
| 10128 |      handle_propfind(conn, path, &file); |
| 10129 |      return; |
| 10130 |    } |
| 10131 |    /* 13.2. Handle OPTIONS for files */ |
| 10132 |    if (!strcmp(ri->request_method, "OPTIONS")) { |
| 10133 |      /* This standard handler is only used for real files. |
| 10134 |       * Scripts should support the OPTIONS method themselves, to allow a |
| 10135 |       * maximum flexibility. |
| 10136 |       * Lua and CGI scripts may fully support CORS this way (including |
| 10137 |       * preflights). */ |
| 10138 |      send_options(conn); |
| 10139 |      return; |
| 10140 |    } |
| 10141 |    /* 13.3. everything but GET and HEAD (e.g. POST) */ |
| 10142 |    if (0 != strcmp(ri->request_method, "GET") |
| 10143 |     && 0 != strcmp(ri->request_method, "HEAD")) { |
| 10144 |      send_http_error(conn, |
| 10145 |       405, |
| 10146 |       "%s method not allowed", |
| 10147 |       conn->request_info.request_method); |
| 10148 |      return; |
| 10149 |    } |
| 10150 | |
| 10151 |    /* 14. directories */ |
| 10152 |    if (file.is_directory) { |
| 10153 |      if (substitute_index_file(conn, path, sizeof(path), &file)) { |
| 10154 |        /* 14.1. use a substitute file */ |
| 10155 |        /* TODO (high): substitute index may be a script resource. |
| 10156 |         * define what should be possible in this case. */ |
| 10157 |      } else { |
| 10158 |        /* 14.2. no substitute file */ |
| 10159 |        if (!mg_strcasecmp(conn->ctx->config[ENABLE_DIRECTORY_LISTING], |
| 10160 |         "yes")) { |
| 10161 |          handle_directory_request(conn, path); |
| 10162 |        } else { |
| 10163 |          send_http_error(conn, |
| 10164 |           403, |
| 10165 |           "%s", |
| 10166 |           "Error: Directory listing denied"); |
| 10167 |        } |
| 10168 |        return; |
| 10169 |      } |
| 10170 |    } |
| 10171 | |
| 10172 |    handle_file_based_request(conn, path, &file); |
| 10173 | #endif /* !defined(NO_FILES) */ |
| 10174 | |
| 10175 | #if 0 |
| 10176 | /* Perform redirect and auth checks before calling begin_request() |
| 10177 | * handler. |
| 10178 | * Otherwise, begin_request() would need to perform auth checks and |
| 10179 | * redirects. */ |
| 10180 | #endif |
| 10181 |  } |
| 10182 |  return; |
| 10183 | } |
| 10184 | |
| 10185 | |
| 10186 | static void |
| 10187 | handle_file_based_request(struct mg_connection *conn, |
| 10188 | const char *path, |
| 10189 | struct file *file) |
| 10190 | { |
| 10191 |  if (!conn || !conn->ctx) { |
| 10192 |    return; |
| 10193 |  } |
| 10194 | |
| 10195 |  if (0) { |
| 10196 | #ifdef USE_LUA |
| 10197 |  } else if (match_prefix(conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS], |
| 10198 |   strlen( |
| 10199 |   conn->ctx->config[LUA_SERVER_PAGE_EXTENSIONS]), |
| 10200 |   path) > 0) { |
| 10201 |    /* Lua server page: an SSI like page containing mostly plain html |
| 10202 |     * code |
| 10203 |     * plus some tags with server generated contents. */ |
| 10204 |    handle_lsp_request(conn, path, file, NULL); |
| 10205 |  } else if (match_prefix(conn->ctx->config[LUA_SCRIPT_EXTENSIONS], |
| 10206 |   strlen(conn->ctx->config[LUA_SCRIPT_EXTENSIONS]), |
| 10207 |   path) > 0) { |
| 10208 |    /* Lua in-server module script: a CGI like script used to generate |
| 10209 |     * the |
| 10210 |     * entire reply. */ |
| 10211 |    mg_exec_lua_script(conn, path, NULL); |
| 10212 | #endif |
| 10213 | #if defined(USE_DUKTAPE) |
| 10214 |  } else if (match_prefix(conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS], |
| 10215 |   strlen( |
| 10216 |   conn->ctx->config[DUKTAPE_SCRIPT_EXTENSIONS]), |
| 10217 |   path) > 0) { |
| 10218 |    /* Call duktape to generate the page */ |
| 10219 |    mg_exec_duktape_script(conn, path); |
| 10220 | #endif |
| 10221 | #if !defined(NO_CGI) |
| 10222 |  } else if (match_prefix(conn->ctx->config[CGI_EXTENSIONS], |
| 10223 |   strlen(conn->ctx->config[CGI_EXTENSIONS]), |
| 10224 |   path) > 0) { |
| 10225 |    /* CGI scripts may support all HTTP methods */ |
| 10226 |    handle_cgi_request(conn, path); |
| 10227 | #endif /* !NO_CGI */ |
| 10228 |  } else if (match_prefix(conn->ctx->config[SSI_EXTENSIONS], |
| 10229 |   strlen(conn->ctx->config[SSI_EXTENSIONS]), |
| 10230 |   path) > 0) { |
| 10231 |    handle_ssi_file_request(conn, path, file); |
| 10232 | #if !defined(NO_CACHING) |
| 10233 |  } else if ((!conn->in_error_handler) && is_not_modified(conn, file)) { |
| 10234 |    /* Send 304 "Not Modified" - this must not send any body data */ |
| 10235 |    send_http_error(conn, 304, "%s", ""); |
| 10236 | #endif /* !NO_CACHING */ |
| 10237 |  } else { |
| 10238 |    handle_static_file_request(conn, path, file, NULL); |
| 10239 |  } |
| 10240 | } |
| 10241 | |
| 10242 | |
| 10243 | static void |
| 10244 | close_all_listening_sockets(struct mg_context *ctx) |
| 10245 | { |
| 10246 |  unsigned int i; |
| 10247 |  if (!ctx) { |
| 10248 |    return; |
| 10249 |  } |
| 10250 | |
| 10251 |  for (i = 0; i < ctx->num_listening_sockets; i++) { |
| 10252 |    closesocket(ctx->listening_sockets[i].sock); |
| 10253 |    ctx->listening_sockets[i].sock = INVALID_SOCKET; |
| 10254 |  } |
| 10255 |  mg_free(ctx->listening_sockets); |
| 10256 |  ctx->listening_sockets = NULL; |
| 10257 |  mg_free(ctx->listening_ports); |
| 10258 |  ctx->listening_ports = NULL; |
| 10259 | } |
| 10260 | |
| 10261 | |
| 10262 | /* Valid listening port specification is: [ip_address:]port[s] |
| 10263 | * Examples for IPv4: 80, 443s, 127.0.0.1:3128, 1.2.3.4:8080s |
| 10264 | * Examples for IPv6: [::]:80, [::1]:80, |
| 10265 | * [FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:443s |
| 10266 | * see https://tools.ietf.org/html/rfc3513#section-2.2 */ |
| 10267 | static int |
| 10268 | parse_port_string(const struct vec *vec, struct socket *so) |
| 10269 | { |
| 10270 |  unsigned int a, b, c, d, port; |
| 10271 |  int ch, len; |
| 10272 | #if defined(USE_IPV6) |
| 10273 |  char buf[100] = {0}; |
| 10274 | #endif |
| 10275 | |
| 10276 |  /* MacOS needs that. If we do not zero it, subsequent bind() will fail. |
| 10277 |   * Also, all-zeroes in the socket address means binding to all addresses |
| 10278 |   * for both IPv4 and IPv6 (INADDR_ANY and IN6ADDR_ANY_INIT). */ |
| 10279 |  memset(so, 0, sizeof(*so)); |
| 10280 |  so->lsa.sin.sin_family = AF_INET; |
| 10281 | |
| 10282 |  if (sscanf(vec->ptr, "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len) |
| 10283 |   == 5) { |
| 10284 |    /* Bind to a specific IPv4 address, e.g. 192.168.1.5:8080 */ |
| 10285 |    so->lsa.sin.sin_addr.s_addr = |
| 10286 |     htonl((a << 24) | (b << 16) | (c << 8) | d); |
| 10287 |    so->lsa.sin.sin_port = htons((uint16_t)port); |
| 10288 | #if defined(USE_IPV6) |
| 10289 |  } else if (sscanf(vec->ptr, "[%49[^]]]:%u%n", buf, &port, &len) == 2 |
| 10290 |   && mg_inet_pton( |
| 10291 |   AF_INET6, buf, &so->lsa.sin6, sizeof(so->lsa.sin6))) { |
| 10292 |    /* IPv6 address, examples: see above */ |
| 10293 |    /* so->lsa.sin6.sin6_family = AF_INET6; already set by mg_inet_pton |
| 10294 |     */ |
| 10295 |    so->lsa.sin6.sin6_port = htons((uint16_t)port); |
| 10296 | #endif |
| 10297 |  } else if (sscanf(vec->ptr, "%u%n", &port, &len) == 1) { |
| 10298 |    /* If only port is specified, bind to IPv4, INADDR_ANY */ |
| 10299 |    so->lsa.sin.sin_port = htons((uint16_t)port); |
| 10300 |  } else { |
| 10301 |    /* Parsing failure. Make port invalid. */ |
| 10302 |    port = 0; |
| 10303 |    len = 0; |
| 10304 |  } |
| 10305 | |
| 10306 |  /* sscanf and the option splitting code ensure the following condition |
| 10307 |   */ |
| 10308 |  if ( ) { |
| 10309 |    return 0; |
| 10310 |  } |
| 10311 |  ch = vec->ptr[len]; /* Next character after the port number */ |
| 10312 |  so->is_ssl = (ch == 's'); |
| 10313 |  so->ssl_redir = (ch == 'r'); |
| 10314 | |
| 10315 |  /* Make sure the port is valid and vector ends with 's', 'r' or ',' */ |
| 10316 |  return is_valid_port(port) |
| 10317 |   && (ch == '\0' || ch == 's' || ch == 'r' || ch == ','); |
| 10318 | } |
| 10319 | |
| 10320 | |
| 10321 | static int |
| 10322 | set_ports_option(struct mg_context *ctx) |
| 10323 | { |
| 10324 |  const char *list; |
| 10325 |  int on = 1; |
| 10326 | #if defined(USE_IPV6) |
| 10327 |  int off = 0; |
| 10328 | #endif |
| 10329 |  struct vec vec; |
| 10330 |  struct socket so, *ptr; |
| 10331 | |
| 10332 |  in_port_t *portPtr; |
| 10333 |  union usa usa; |
| 10334 |  socklen_t len; |
| 10335 | |
| 10336 |  int portsTotal = 0; |
| 10337 |  int portsOk = 0; |
| 10338 | |
| 10339 |  if (!ctx) { |
| 10340 |    return 0; |
| 10341 |  } |
| 10342 | |
| 10343 |  memset(&so, 0, sizeof(so)); |
| 10344 |  memset(&usa, 0, sizeof(usa)); |
| 10345 |  len = sizeof(usa); |
| 10346 |  list = ctx->config[LISTENING_PORTS]; |
| 10347 |  while ((list = next_option(list, &vec, NULL)) != NULL) { |
| 10348 | |
| 10349 |    portsTotal++; |
| 10350 | |
| 10351 |    if (!parse_port_string(&vec, &so)) { |
| 10352 |      mg_cry(fc(ctx), |
| 10353 |       "%.*s: invalid port spec (entry %i). Expecting list of: %s", |
| 10354 |       (int)vec.len, |
| 10355 |       vec.ptr, |
| 10356 |       portsTotal, |
| 10357 |       "[IP_ADDRESS:]PORT[s|r]"); |
| 10358 |      continue; |
| 10359 |    } |
| 10360 | |
| 10361 |    if (so.is_ssl && ctx->ssl_ctx == NULL) { |
| 10362 | |
| 10363 |      mg_cry(fc(ctx), |
| 10364 |       "Cannot add SSL socket (entry %i). Is -ssl_certificate " |
| 10365 |       "option set?", |
| 10366 |       portsTotal); |
| 10367 |      continue; |
| 10368 |    } |
| 10369 | |
| 10370 |    if ((so.sock = socket(so.lsa.sa.sa_family, SOCK_STREAM, 6)) |
| 10371 |     == INVALID_SOCKET) { |
| 10372 | |
| 10373 |      mg_cry(fc(ctx), "cannot create socket (entry %i)", portsTotal); |
| 10374 |      continue; |
| 10375 |    } |
| 10376 | |
| 10377 | #ifdef _WIN32 |
| 10378 |    /* Windows SO_REUSEADDR lets many procs binds to a |
| 10379 |     * socket, SO_EXCLUSIVEADDRUSE makes the bind fail |
| 10380 |     * if someone already has the socket -- DTL */ |
| 10381 |    /* NOTE: If SO_EXCLUSIVEADDRUSE is used, |
| 10382 |     * Windows might need a few seconds before |
| 10383 |     * the same port can be used again in the |
| 10384 |     * same process, so a short Sleep may be |
| 10385 |     * required between mg_stop and mg_start. |
| 10386 |     */ |
| 10387 |    if (setsockopt(so.sock, |
| 10388 |     SOL_SOCKET, |
| 10389 |     SO_EXCLUSIVEADDRUSE, |
| 10390 |     (SOCK_OPT_TYPE)&on, |
| 10391 |     sizeof(on)) != 0) { |
| 10392 | |
| 10393 |      mg_cry(fc(ctx), |
| 10394 |       "cannot set socket option SO_EXCLUSIVEADDRUSE (entry %i)", |
| 10395 |       portsTotal); |
| 10396 |    } |
| 10397 | #else |
| 10398 |    if (setsockopt(so.sock, |
| 10399 |     SOL_SOCKET, |
| 10400 |     SO_REUSEADDR, |
| 10401 |     (SOCK_OPT_TYPE)&on, |
| 10402 |     sizeof(on)) != 0) { |
| 10403 | |
| 10404 |      mg_cry(fc(ctx), |
| 10405 |       "cannot set socket option SO_REUSEADDR (entry %i)", |
| 10406 |       portsTotal); |
| 10407 |    } |
| 10408 | #endif |
| 10409 | |
| 10410 | #if defined(USE_IPV6) |
| 10411 |    if (so.lsa.sa.sa_family == AF_INET6 |
| 10412 |     && setsockopt(so.sock, |
| 10413 |     IPPROTO_IPV6, |
| 10414 |     IPV6_V6ONLY, |
| 10415 |     (void *)&off, |
| 10416 |     sizeof(off)) != 0) { |
| 10417 | |
| 10418 |      mg_cry(fc(ctx), |
| 10419 |       "cannot set socket option IPV6_V6ONLY (entry %i)", |
| 10420 |       portsTotal); |
| 10421 |    } |
| 10422 | #endif |
| 10423 | |
| 10424 |    if (so.lsa.sa.sa_family == AF_INET) { |
| 10425 | |
| 10426 |      len = sizeof(so.lsa.sin); |
| 10427 |      if (bind(so.sock, &so.lsa.sa, len) != 0) { |
| 10428 |        mg_cry(fc(ctx), |
| 10429 |         "cannot bind to %.*s: %d (%s)", |
| 10430 |         (int)vec.len, |
| 10431 |         vec.ptr, |
| 10432 |         (int)ERRNO, |
| 10433 |         strerror(errno)); |
| 10434 |        closesocket(so.sock); |
| 10435 |        so.sock = INVALID_SOCKET; |
| 10436 |        continue; |
| 10437 |      } |
| 10438 |    } |
| 10439 | #if defined(USE_IPV6) |
| 10440 |    else if (so.lsa.sa.sa_family == AF_INET6) { |
| 10441 | |
| 10442 |      len = sizeof(so.lsa.sin6); |
| 10443 |      if (bind(so.sock, &so.lsa.sa, len) != 0) { |
| 10444 |        mg_cry(fc(ctx), |
| 10445 |         "cannot bind to IPv6 %.*s: %d (%s)", |
| 10446 |         (int)vec.len, |
| 10447 |         vec.ptr, |
| 10448 |         (int)ERRNO, |
| 10449 |         strerror(errno)); |
| 10450 |        closesocket(so.sock); |
| 10451 |        so.sock = INVALID_SOCKET; |
| 10452 |        continue; |
| 10453 |      } |
| 10454 |    } |
| 10455 | #endif |
| 10456 |    else { |
| 10457 |      mg_cry(fc(ctx), |
| 10458 |       "cannot bind: address family not supported (entry %i)", |
| 10459 |       portsTotal); |
| 10460 |      continue; |
| 10461 |    } |
| 10462 | |
| 10463 |    if (listen(so.sock, SOMAXCONN) != 0) { |
| 10464 | |
| 10465 |      mg_cry(fc(ctx), |
| 10466 |       "cannot listen to %.*s: %d (%s)", |
| 10467 |       (int)vec.len, |
| 10468 |       vec.ptr, |
| 10469 |       (int)ERRNO, |
| 10470 |       strerror(errno)); |
| 10471 |      closesocket(so.sock); |
| 10472 |      so.sock = INVALID_SOCKET; |
| 10473 |      continue; |
| 10474 |    } |
| 10475 | |
| 10476 |    if (getsockname(so.sock, &(usa.sa), &len) != 0) { |
| 10477 | |
| 10478 |      int err = (int)ERRNO; |
| 10479 |      mg_cry(fc(ctx), |
| 10480 |       "call to getsockname failed %.*s: %d (%s)", |
| 10481 |       (int)vec.len, |
| 10482 |       vec.ptr, |
| 10483 |       err, |
| 10484 |       strerror(errno)); |
| 10485 |      closesocket(so.sock); |
| 10486 |      so.sock = INVALID_SOCKET; |
| 10487 |      continue; |
| 10488 |    } |
| 10489 | |
| 10490 |    if ((ptr = (struct socket *) |
| 10491 |     mg_realloc(ctx->listening_sockets, |
| 10492 |     (ctx->num_listening_sockets + 1) |
| 10493 |     * sizeof(ctx->listening_sockets[0]))) == NULL) { |
| 10494 | |
| 10495 |      mg_cry(fc(ctx), "%s", "Out of memory"); |
| 10496 |      closesocket(so.sock); |
| 10497 |      so.sock = INVALID_SOCKET; |
| 10498 |      continue; |
| 10499 |    } |
| 10500 | |
| 10501 |    if ((portPtr = |
| 10502 |     (in_port_t *)mg_realloc(ctx->listening_ports, |
| 10503 |     (ctx->num_listening_sockets + 1) |
| 10504 |     * sizeof(ctx->listening_ports[0]))) |
| 10505 |     == NULL) { |
| 10506 | |
| 10507 |      mg_cry(fc(ctx), "%s", "Out of memory"); |
| 10508 |      closesocket(so.sock); |
| 10509 |      so.sock = INVALID_SOCKET; |
| 10510 |      mg_free(ptr); |
| 10511 |      continue; |
| 10512 |    } |
| 10513 | |
| 10514 |    set_close_on_exec(so.sock, fc(ctx)); |
| 10515 |    ctx->listening_sockets = ptr; |
| 10516 |    ctx->listening_sockets[ctx->num_listening_sockets] = so; |
| 10517 |    ctx->listening_ports = portPtr; |
| 10518 |    ctx->listening_ports[ctx->num_listening_sockets] = |
| 10519 |     ntohs(usa.sin.sin_port); |
| 10520 |    ctx->num_listening_sockets++; |
| 10521 |    portsOk++; |
| 10522 |  } |
| 10523 | |
| 10524 |  if (portsOk != portsTotal) { |
| 10525 |    close_all_listening_sockets(ctx); |
| 10526 |    portsOk = 0; |
| 10527 |  } |
| 10528 | |
| 10529 |  return portsOk; |
| 10530 | } |
| 10531 | |
| 10532 | |
| 10533 | static const char * |
| 10534 | header_val(const struct mg_connection *conn, const char *header) |
| 10535 | { |
| 10536 |  const char *header_value; |
| 10537 | |
| 10538 |  if ((header_value = mg_get_header(conn, header)) == NULL) { |
| 10539 |    return "-"; |
| 10540 |  } else { |
| 10541 |    return header_value; |
| 10542 |  } |
| 10543 | } |
| 10544 | |
| 10545 | |
| 10546 | static void |
| 10547 | log_access(const struct mg_connection *conn) |
| 10548 | { |
| 10549 |  const struct mg_request_info *ri; |
| 10550 |  struct file fi; |
| 10551 |  char date[64], src_addr[IP_ADDR_STR_LEN]; |
| 10552 |  struct tm *tm; |
| 10553 | |
| 10554 |  const char *referer; |
| 10555 |  const char *user_agent; |
| 10556 | |
| 10557 |  char buf[4096]; |
| 10558 | |
| 10559 |  if (!conn || !conn->ctx) { |
| 10560 |    return; |
| 10561 |  } |
| 10562 | |
| 10563 |  if (conn->ctx->config[ACCESS_LOG_FILE] != NULL) { |
| 10564 |    if (mg_fopen(conn, conn->ctx->config[ACCESS_LOG_FILE], "a+", &fi) |
| 10565 |     == 0) { |
| 10566 |      fi.fp = NULL; |
| 10567 |    } |
| 10568 |  } else { |
| 10569 |    fi.fp = NULL; |
| 10570 |  } |
| 10571 | |
| 10572 |  if (fi.fp == NULL && conn->ctx->callbacks.log_message == NULL) { |
| 10573 |    return; |
| 10574 |  } |
| 10575 | |
| 10576 |  tm = localtime(&conn->conn_birth_time); |
| 10577 |  if (tm != NULL) { |
| 10578 |    strftime(date, sizeof(date), "%d/%b/%Y:%H:%M:%S %z", tm); |
| 10579 |  } else { |
| 10580 |    mg_strlcpy(date, "01/Jan/1970:00:00:00 +0000", sizeof(date)); |
| 10581 |    date[sizeof(date) - 1] = '\0'; |
| 10582 |  } |
| 10583 | |
| 10584 |  ri = &conn->request_info; |
| 10585 | |
| 10586 |  sockaddr_to_string(src_addr, sizeof(src_addr), &conn->client.rsa); |
| 10587 |  referer = header_val(conn, "Referer"); |
| 10588 |  user_agent = header_val(conn, "User-Agent"); |
| 10589 | |
| 10590 |  mg_snprintf(conn, |
| 10591 |   NULL, /* Ignore truncation in access log */ |
| 10592 |   buf, |
| 10593 |   sizeof(buf), |
| 10594 |   "%s - %s [%s] \"%s %s%s%s HTTP/%s\" %d %" INT64_FMT " %s %s", |
| 10595 |   src_addr, |
| 10596 |   ri->remote_user == NULL ? "-" : ri->remote_user, |
| 10597 |   date, |
| 10598 |   ri->request_method ? ri->request_method : "-", |
| 10599 |   ri->request_uri ? ri->request_uri : "-", |
| 10600 |   ri->query_string ? "?" : "", |
| 10601 |   ri->query_string ? ri->query_string : "", |
| 10602 |   ri->http_version, |
| 10603 |   conn->status_code, |
| 10604 |   conn->num_bytes_sent, |
| 10605 |   referer, |
| 10606 |   user_agent); |
| 10607 | |
| 10608 |  if (conn->ctx->callbacks.log_access) { |
| 10609 |    conn->ctx->callbacks.log_access(conn, buf); |
| 10610 |  } |
| 10611 | |
| 10612 |  if (fi.fp) { |
| 10613 |    flockfile(fi.fp); |
| 10614 |    fprintf(fi.fp, "%s\n", buf); |
| 10615 |    fflush(fi.fp); |
| 10616 |    funlockfile(fi.fp); |
| 10617 |    mg_fclose(&fi); |
| 10618 |  } |
| 10619 | } |
| 10620 | |
| 10621 | |
| 10622 | /* Verify given socket address against the ACL. |
| 10623 | * Return -1 if ACL is malformed, 0 if address is disallowed, 1 if allowed. |
| 10624 | */ |
| 10625 | static int |
| 10626 | check_acl(struct mg_context *ctx, uint32_t remote_ip) |
| 10627 | { |
| 10628 |  int allowed, flag; |
| 10629 |  uint32_t net, mask; |
| 10630 |  struct vec vec; |
| 10631 | |
| 10632 |  if (ctx) { |
| 10633 |    const char *list = ctx->config[ACCESS_CONTROL_LIST]; |
| 10634 | |
| 10635 |    /* If any ACL is set, deny by default */ |
| 10636 |    allowed = list == NULL ? '+' : '-'; |
| 10637 | |
| 10638 |    while ((list = next_option(list, &vec, NULL)) != NULL) { |
| 10639 |      flag = vec.ptr[0]; |
| 10640 |      if ((flag != '+' && flag != '-') |
| 10641 |       || parse_net(&vec.ptr[1], &net, &mask) == 0) { |
| 10642 |        mg_cry(fc(ctx), |
| 10643 |         "%s: subnet must be [+|-]x.x.x.x[/x]", |
| 10644 |         __func__); |
| 10645 |        return -1; |
| 10646 |      } |
| 10647 | |
| 10648 |      if (net == (remote_ip & mask)) { |
| 10649 |        allowed = flag; |
| 10650 |      } |
| 10651 |    } |
| 10652 | |
| 10653 |    return allowed == '+'; |
| 10654 |  } |
| 10655 |  return -1; |
| 10656 | } |
| 10657 | |
| 10658 | |
| 10659 | #if !defined(_WIN32) |
| 10660 | static int |
| 10661 | set_uid_option(struct mg_context *ctx) |
| 10662 | { |
| 10663 |  struct passwd *pw; |
| 10664 |  if (ctx) { |
| 10665 |    const char *uid = ctx->config[RUN_AS_USER]; |
| 10666 |    int success = 0; |
| 10667 | |
| 10668 |    if (uid == NULL) { |
| 10669 |      success = 1; |
| 10670 |    } else { |
| 10671 |      if ((pw = getpwnam(uid)) == NULL) { |
| 10672 |        mg_cry(fc(ctx), "%s: unknown user [%s]", __func__, uid); |
| 10673 |      } else if (setgid(pw->pw_gid) == -1) { |
| 10674 |        mg_cry(fc(ctx), |
| 10675 |         "%s: setgid(%s): %s", |
| 10676 |         __func__, |
| 10677 |         uid, |
| 10678 |         strerror(errno)); |
| 10679 |      } else if (setgroups(0, NULL)) { |
| 10680 |        mg_cry(fc(ctx), |
| 10681 |         "%s: setgroups(): %s", |
| 10682 |         __func__, |
| 10683 |         strerror(errno)); |
| 10684 |      } else if (setuid(pw->pw_uid) == -1) { |
| 10685 |        mg_cry(fc(ctx), |
| 10686 |         "%s: setuid(%s): %s", |
| 10687 |         __func__, |
| 10688 |         uid, |
| 10689 |         strerror(errno)); |
| 10690 |      } else { |
| 10691 |        success = 1; |
| 10692 |      } |
| 10693 |    } |
| 10694 | |
| 10695 |    return success; |
| 10696 |  } |
| 10697 |  return 0; |
| 10698 | } |
| 10699 | #endif /* !_WIN32 */ |
| 10700 | |
| 10701 | |
| 10702 | static void |
| 10703 | tls_dtor(void *key) |
| 10704 | { |
| 10705 |  struct mg_workerTLS *tls = (struct mg_workerTLS *)key; |
| 10706 |  /* key == pthread_getspecific(sTlsKey); */ |
| 10707 | |
| 10708 |  if (tls) { |
| 10709 |    if (tls->is_master == 2) { |
| 10710 |      tls->is_master = -3; /* Mark memory as dead */ |
| 10711 |      mg_free(tls); |
| 10712 |    } |
| 10713 |  } |
| 10714 |  pthread_setspecific(sTlsKey, NULL); |
| 10715 | } |
| 10716 | |
| 10717 | |
| 10718 | #if !defined(NO_SSL) |
| 10719 | |
| 10720 | /* Must be set if sizeof(pthread_t) > sizeof(unsigned long) */ |
| 10721 | static unsigned long |
| 10722 | ssl_id_callback(void) |
| 10723 | { |
| 10724 | #ifdef _WIN32 |
| 10725 |  return GetCurrentThreadId(); |
| 10726 | #else |
| 10727 | |
| 10728 | #ifdef __clang__ |
| 10729 | #pragma clang diagnostic push |
| 10730 | #pragma clang diagnostic ignored "-Wunreachable-code" |
| 10731 | /* For every compiler, either "sizeof(pthread_t) > sizeof(unsigned long)" |
| 10732 | * or not, so one of the two conditions will be unreachable by construction. |
| 10733 | * Unfortunately the C standard does not define a way to check this at |
| 10734 | * compile time, since the #if preprocessor conditions can not use the sizeof |
| 10735 | * operator as an argument. */ |
| 10736 | #endif |
| 10737 | |
| 10738 |  if (sizeof(pthread_t) > sizeof(unsigned long)) { |
| 10739 |    /* This is the problematic case for CRYPTO_set_id_callback: |
| 10740 |     * The OS pthread_t can not be cast to unsigned long. */ |
| 10741 |    struct mg_workerTLS *tls = |
| 10742 |     (struct mg_workerTLS *)pthread_getspecific(sTlsKey); |
| 10743 |    if (tls == NULL) { |
| 10744 |      /* SSL called from an unknown thread: Create some thread index. |
| 10745 |       */ |
| 10746 |      tls = (struct mg_workerTLS *)mg_malloc(sizeof(struct mg_workerTLS)); |
| 10747 |      tls->is_master = -2; /* -2 means "3rd party thread" */ |
| 10748 |      tls->thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); |
| 10749 |      pthread_setspecific(sTlsKey, tls); |
| 10750 |    } |
| 10751 |    return tls->thread_idx; |
| 10752 |  } else { |
| 10753 |    /* pthread_t may be any data type, so a simple cast to unsigned long |
| 10754 |     * can rise a warning/error, depending on the platform. |
| 10755 |     * Here memcpy is used as an anything-to-anything cast. */ |
| 10756 |    unsigned long ret = 0; |
| 10757 |    pthread_t t = pthread_self(); |
| 10758 |    memcpy(&ret, &t, sizeof(pthread_t)); |
| 10759 |    return ret; |
| 10760 |  } |
| 10761 | |
| 10762 | #ifdef __clang__ |
| 10763 | #pragma clang diagnostic pop |
| 10764 | #endif |
| 10765 | |
| 10766 | #endif |
| 10767 | } |
| 10768 | |
| 10769 | |
| 10770 | static int ssl_use_pem_file(struct mg_context *ctx, const char *pem); |
| 10771 | static const char *ssl_error(void); |
| 10772 | |
| 10773 | |
| 10774 | static int |
| 10775 | refresh_trust(struct mg_connection *conn) |
| 10776 | { |
| 10777 |  static int reload_lock = 0; |
| 10778 |  static long int data_check = 0; |
| 10779 | |
| 10780 |  struct stat cert_buf; |
| 10781 |  long int t; |
| 10782 |  char *pem; |
| 10783 |  int should_verify_peer; |
| 10784 | |
| 10785 |  if ((pem = conn->ctx->config[SSL_CERTIFICATE]) == NULL |
| 10786 |   && conn->ctx->callbacks.init_ssl == NULL) { |
| 10787 |    return 0; |
| 10788 |  } |
| 10789 | |
| 10790 |  t = data_check; |
| 10791 |  if (stat(pem, &cert_buf) != -1) { |
| 10792 |    t = (long int)cert_buf.st_mtime; |
| 10793 |  } |
| 10794 | |
| 10795 |  if (data_check != t) { |
| 10796 |    data_check = t; |
| 10797 | |
| 10798 |    should_verify_peer = |
| 10799 |     (conn->ctx->config[SSL_DO_VERIFY_PEER] != NULL) |
| 10800 |     && (mg_strcasecmp(conn->ctx->config[SSL_DO_VERIFY_PEER], "yes") |
| 10801 |     == 0); |
| 10802 | |
| 10803 |    if (should_verify_peer) { |
| 10804 |      char *ca_path = conn->ctx->config[SSL_CA_PATH]; |
| 10805 |      char *ca_file = conn->ctx->config[SSL_CA_FILE]; |
| 10806 |      if (SSL_CTX_load_verify_locations(conn->ctx->ssl_ctx, |
| 10807 |       ca_file, |
| 10808 |       ca_path) != 1) { |
| 10809 |        mg_cry(fc(conn->ctx), |
| 10810 |         "SSL_CTX_load_verify_locations error: %s " |
| 10811 |         "ssl_verify_peer requires setting " |
| 10812 |         "either ssl_ca_path or ssl_ca_file. Is any of them " |
| 10813 |         "present in " |
| 10814 |         "the .conf file?", |
| 10815 |         ssl_error()); |
| 10816 |        return 0; |
| 10817 |      } |
| 10818 |    } |
| 10819 | |
| 10820 |    if (!reload_lock) { |
| 10821 |      reload_lock = 1; |
| 10822 |      if (ssl_use_pem_file(conn->ctx, pem) == 0) { |
| 10823 |        return 0; |
| 10824 |      } |
| 10825 |      reload_lock = 0; |
| 10826 |    } |
| 10827 |  } |
| 10828 |  /* lock while cert is reloading */ |
| 10829 |  while (reload_lock) { |
| 10830 |    sleep(1); |
| 10831 |  } |
| 10832 | |
| 10833 |  return 1; |
| 10834 | } |
| 10835 | |
| 10836 | |
| 10837 | static pthread_mutex_t *ssl_mutexes; |
| 10838 | |
| 10839 | |
| 10840 | static int |
| 10841 | sslize(struct mg_connection *conn, SSL_CTX *s, int (*func)(SSL *)) |
| 10842 | { |
| 10843 |  int ret, err; |
| 10844 |  int short_trust; |
| 10845 | |
| 10846 |  if (!conn) { |
| 10847 |    return 0; |
| 10848 |  } |
| 10849 | |
| 10850 |  short_trust = |
| 10851 |   (conn->ctx->config[SSL_SHORT_TRUST] != NULL) |
| 10852 |   && (mg_strcasecmp(conn->ctx->config[SSL_SHORT_TRUST], "yes") == 0); |
| 10853 | |
| 10854 |  if (short_trust) { |
| 10855 |    int trust_ret = refresh_trust(conn); |
| 10856 |    if (!trust_ret) { |
| 10857 |      return trust_ret; |
| 10858 |    } |
| 10859 |  } |
| 10860 | |
| 10861 |  conn->ssl = SSL_new(s); |
| 10862 |  if (conn->ssl == NULL) { |
| 10863 |    return 0; |
| 10864 |  } |
| 10865 | |
| 10866 |  ret = SSL_set_fd(conn->ssl, conn->client.sock); |
| 10867 |  if (ret != 1) { |
| 10868 |    err = SSL_get_error(conn->ssl, ret); |
| 10869 |    (void)err; /* TODO: set some error message */ |
| 10870 |    SSL_free(conn->ssl); |
| 10871 |    conn->ssl = NULL; |
| 10872 |    /* maybe not? CRYPTO_cleanup_all_ex_data(); */ |
| 10873 |    /* see |
| 10874 |     * https://wiki.openssl.org/index.php/Talk:Library_Initialization */ |
| 10875 |    ERR_remove_state(0); |
| 10876 |    return 0; |
| 10877 |  } |
| 10878 | |
| 10879 |  ret = func(conn->ssl); |
| 10880 |  if (ret != 1) { |
| 10881 |    err = SSL_get_error(conn->ssl, ret); |
| 10882 |    (void)err; /* TODO: set some error message */ |
| 10883 |    SSL_free(conn->ssl); |
| 10884 |    conn->ssl = NULL; |
| 10885 |    /* maybe not? CRYPTO_cleanup_all_ex_data(); */ |
| 10886 |    /* see |
| 10887 |     * https://wiki.openssl.org/index.php/Talk:Library_Initialization */ |
| 10888 |    ERR_remove_state(0); |
| 10889 |    return 0; |
| 10890 |  } |
| 10891 | |
| 10892 |  return 1; |
| 10893 | } |
| 10894 | |
| 10895 | |
| 10896 | /* Return OpenSSL error message (from CRYPTO lib) */ |
| 10897 | static const char * |
| 10898 | ssl_error(void) |
| 10899 | { |
| 10900 |  unsigned long err; |
| 10901 |  err = ERR_get_error(); |
| 10902 |  return err == 0 ? "" : ERR_error_string(err, NULL); |
| 10903 | } |
| 10904 | |
| 10905 | |
| 10906 | static void |
| 10907 | ssl_locking_callback(int mode, int mutex_num, const char *file, int line) |
| 10908 | { |
| 10909 |  (void)line; |
| 10910 |  (void)file; |
| 10911 | |
| 10912 |  if (mode & 1) { |
| 10913 |    /* 1 is CRYPTO_LOCK */ |
| 10914 |    (void)pthread_mutex_lock(&ssl_mutexes[mutex_num]); |
| 10915 |  } else { |
| 10916 |    (void)pthread_mutex_unlock(&ssl_mutexes[mutex_num]); |
| 10917 |  } |
| 10918 | } |
| 10919 | |
| 10920 | |
| 10921 | #if !defined(NO_SSL_DL) |
| 10922 | static void * |
| 10923 | load_dll(struct mg_context *ctx, const char *dll_name, struct ssl_func *sw) |
| 10924 | { |
| 10925 |  union { |
| 10926 |    void *p; |
| 10927 |    void (*fp)(void); |
| 10928 |  } u; |
| 10929 |  void *dll_handle; |
| 10930 |  struct ssl_func *fp; |
| 10931 | |
| 10932 |  if ((dll_handle = dlopen(dll_name, RTLD_LAZY)) == NULL) { |
| 10933 |    mg_cry(fc(ctx), "%s: cannot load %s", __func__, dll_name); |
| 10934 |    return NULL; |
| 10935 |  } |
| 10936 | |
| 10937 |  for (fp = sw; fp->name != NULL; fp++) { |
| 10938 | #ifdef _WIN32 |
| 10939 |    /* GetProcAddress() returns pointer to function */ |
| 10940 |    u.fp = (void (*)(void))dlsym(dll_handle, fp->name); |
| 10941 | #else |
| 10942 |    /* dlsym() on UNIX returns void *. ISO C forbids casts of data |
| 10943 |     * pointers to function pointers. We need to use a union to make a |
| 10944 |     * cast. */ |
| 10945 |    u.p = dlsym(dll_handle, fp->name); |
| 10946 | #endif /* _WIN32 */ |
| 10947 |    if (u.fp == NULL) { |
| 10948 |      mg_cry(fc(ctx), |
| 10949 |       "%s: %s: cannot find %s", |
| 10950 |       __func__, |
| 10951 |       dll_name, |
| 10952 |       fp->name); |
| 10953 |      dlclose(dll_handle); |
| 10954 |      return NULL; |
| 10955 |    } else { |
| 10956 |      fp->ptr = u.fp; |
| 10957 |    } |
| 10958 |  } |
| 10959 | |
| 10960 |  return dll_handle; |
| 10961 | } |
| 10962 | |
| 10963 | |
| 10964 | static void *ssllib_dll_handle; /* Store the ssl library handle. */ |
| 10965 | static void *cryptolib_dll_handle; /* Store the crypto library handle. */ |
| 10966 | |
| 10967 | #endif /* NO_SSL_DL */ |
| 10968 | |
| 10969 | |
| 10970 | #if defined(SSL_ALREADY_INITIALIZED) |
| 10971 | static int cryptolib_users = 1; /* Reference counter for crypto library. */ |
| 10972 | #else |
| 10973 | static int cryptolib_users = 0; /* Reference counter for crypto library. */ |
| 10974 | #endif |
| 10975 | |
| 10976 | |
| 10977 | static int |
| 10978 | initialize_ssl(struct mg_context *ctx) |
| 10979 | { |
| 10980 |  int i; |
| 10981 |  size_t size; |
| 10982 | |
| 10983 | #if !defined(NO_SSL_DL) |
| 10984 |  if (!cryptolib_dll_handle) { |
| 10985 |    cryptolib_dll_handle = load_dll(ctx, CRYPTO_LIB, crypto_sw); |
| 10986 |    if (!cryptolib_dll_handle) { |
| 10987 |      return 0; |
| 10988 |    } |
| 10989 |  } |
| 10990 | #endif /* NO_SSL_DL */ |
| 10991 | |
| 10992 |  if (mg_atomic_inc(&cryptolib_users) > 1) { |
| 10993 |    return 1; |
| 10994 |  } |
| 10995 | |
| 10996 |  /* Initialize locking callbacks, needed for thread safety. |
| 10997 |   * http://www.openssl.org/support/faq.html#PROG1 |
| 10998 |   */ |
| 10999 |  i = CRYPTO_num_locks(); |
| 11000 |  if (i < 0) { |
| 11001 |    i = 0; |
| 11002 |  } |
| 11003 |  size = sizeof(pthread_mutex_t) * ((size_t)(i)); |
| 11004 |  if ((ssl_mutexes = (pthread_mutex_t *)mg_malloc(size)) == NULL) { |
| 11005 |    mg_cry(fc(ctx), |
| 11006 |     "%s: cannot allocate mutexes: %s", |
| 11007 |     __func__, |
| 11008 |     ssl_error()); |
| 11009 |    return 0; |
| 11010 |  } |
| 11011 | |
| 11012 |  for (i = 0; i < CRYPTO_num_locks(); i++) { |
| 11013 |    pthread_mutex_init(&ssl_mutexes[i], &pthread_mutex_attr); |
| 11014 |  } |
| 11015 | |
| 11016 |  CRYPTO_set_locking_callback(&ssl_locking_callback); |
| 11017 |  CRYPTO_set_id_callback(&ssl_id_callback); |
| 11018 | |
| 11019 |  return 1; |
| 11020 | } |
| 11021 | |
| 11022 | |
| 11023 | static int |
| 11024 | ssl_use_pem_file(struct mg_context *ctx, const char *pem) |
| 11025 | { |
| 11026 |  if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0) { |
| 11027 |    mg_cry(fc(ctx), |
| 11028 |     "%s: cannot open certificate file %s: %s", |
| 11029 |     __func__, |
| 11030 |     pem, |
| 11031 |     ssl_error()); |
| 11032 |    return 0; |
| 11033 |  } |
| 11034 | |
| 11035 |  /* could use SSL_CTX_set_default_passwd_cb_userdata */ |
| 11036 |  if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0) { |
| 11037 |    mg_cry(fc(ctx), |
| 11038 |     "%s: cannot open private key file %s: %s", |
| 11039 |     __func__, |
| 11040 |     pem, |
| 11041 |     ssl_error()); |
| 11042 |    return 0; |
| 11043 |  } |
| 11044 | |
| 11045 |  if (SSL_CTX_check_private_key(ctx->ssl_ctx) == 0) { |
| 11046 |    mg_cry(fc(ctx), |
| 11047 |     "%s: certificate and private key do not match: %s", |
| 11048 |     __func__, |
| 11049 |     pem); |
| 11050 |    return 0; |
| 11051 |  } |
| 11052 | |
| 11053 |  if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem) == 0) { |
| 11054 |    mg_cry(fc(ctx), |
| 11055 |     "%s: cannot use certificate chain file %s: %s", |
| 11056 |     __func__, |
| 11057 |     pem, |
| 11058 |     ssl_error()); |
| 11059 |    return 0; |
| 11060 |  } |
| 11061 |  return 1; |
| 11062 | } |
| 11063 | |
| 11064 | |
| 11065 | static long |
| 11066 | ssl_get_protocol(int version_id) |
| 11067 | { |
| 11068 |  long ret = SSL_OP_ALL; |
| 11069 |  if (version_id > 0) |
| 11070 |    ret |= SSL_OP_NO_SSLv2; |
| 11071 |  if (version_id > 1) |
| 11072 |    ret |= SSL_OP_NO_SSLv3; |
| 11073 |  if (version_id > 2) |
| 11074 |    ret |= SSL_OP_NO_TLSv1; |
| 11075 |  if (version_id > 3) |
| 11076 |    ret |= SSL_OP_NO_TLSv1_1; |
| 11077 |  return ret; |
| 11078 | } |
| 11079 | |
| 11080 | |
| 11081 | /* Dynamically load SSL library. Set up ctx->ssl_ctx pointer. */ |
| 11082 | static int |
| 11083 | set_ssl_option(struct mg_context *ctx) |
| 11084 | { |
| 11085 |  const char *pem; |
| 11086 |  int callback_ret; |
| 11087 |  int should_verify_peer; |
| 11088 |  const char *ca_path; |
| 11089 |  const char *ca_file; |
| 11090 |  int use_default_verify_paths; |
| 11091 |  int verify_depth; |
| 11092 |  time_t now_rt = time(NULL); |
| 11093 |  struct timespec now_mt; |
| 11094 |  md5_byte_t ssl_context_id[16]; |
| 11095 |  md5_state_t md5state; |
| 11096 |  int protocol_ver; |
| 11097 | |
| 11098 |  /* If PEM file is not specified and the init_ssl callback |
| 11099 |   * is not specified, skip SSL initialization. */ |
| 11100 |  if (!ctx) { |
| 11101 |    return 0; |
| 11102 |  } |
| 11103 |  if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL |
| 11104 |   && ctx->callbacks.init_ssl == NULL) { |
| 11105 |    return 1; |
| 11106 |  } |
| 11107 | |
| 11108 |  if (!initialize_ssl(ctx)) { |
| 11109 |    return 0; |
| 11110 |  } |
| 11111 | |
| 11112 | #if !defined(NO_SSL_DL) |
| 11113 |  if (!ssllib_dll_handle) { |
| 11114 |    ssllib_dll_handle = load_dll(ctx, SSL_LIB, ssl_sw); |
| 11115 |    if (!ssllib_dll_handle) { |
| 11116 |      return 0; |
| 11117 |    } |
| 11118 |  } |
| 11119 | #endif /* NO_SSL_DL */ |
| 11120 | |
| 11121 |  /* Initialize SSL library */ |
| 11122 |  SSL_library_init(); |
| 11123 |  SSL_load_error_strings(); |
| 11124 | |
| 11125 |  if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { |
| 11126 |    mg_cry(fc(ctx), "SSL_CTX_new (server) error: %s", ssl_error()); |
| 11127 |    return 0; |
| 11128 |  } |
| 11129 | |
| 11130 |  SSL_CTX_clear_options(ctx->ssl_ctx, |
| 11131 |   SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 |
| 11132 |   | SSL_OP_NO_TLSv1_1); |
| 11133 |  protocol_ver = atoi(ctx->config[SSL_PROTOCOL_VERSION]); |
| 11134 |  SSL_CTX_set_options(ctx->ssl_ctx, ssl_get_protocol(protocol_ver)); |
| 11135 |  SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_SINGLE_DH_USE); |
| 11136 |  SSL_CTX_set_ecdh_auto(ctx->ssl_ctx, 1); |
| 11137 | |
| 11138 |  /* If a callback has been specified, call it. */ |
| 11139 |  callback_ret = |
| 11140 |   (ctx->callbacks.init_ssl == NULL) |
| 11141 |   ? 0 |
| 11142 |   : (ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)); |
| 11143 | |
| 11144 |  /* If callback returns 0, civetweb sets up the SSL certificate. |
| 11145 |   * If it returns 1, civetweb assumes the calback already did this. |
| 11146 |   * If it returns -1, initializing ssl fails. */ |
| 11147 |  if (callback_ret < 0) { |
| 11148 |    mg_cry(fc(ctx), "SSL callback returned error: %i", callback_ret); |
| 11149 |    return 0; |
| 11150 |  } |
| 11151 |  if (callback_ret > 0) { |
| 11152 |    if (pem != NULL) { |
| 11153 |      (void)SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem); |
| 11154 |    } |
| 11155 |    return 1; |
| 11156 |  } |
| 11157 | |
| 11158 |  /* Use some UID as session context ID. */ |
| 11159 |  md5_init(&md5state); |
| 11160 |  md5_append(&md5state, (const md5_byte_t *)&now_rt, sizeof(now_rt)); |
| 11161 |  clock_gettime(CLOCK_MONOTONIC, &now_mt); |
| 11162 |  md5_append(&md5state, (const md5_byte_t *)&now_mt, sizeof(now_mt)); |
| 11163 |  md5_append(&md5state, |
| 11164 |   (const md5_byte_t *)ctx->config[LISTENING_PORTS], |
| 11165 |   strlen(ctx->config[LISTENING_PORTS])); |
| 11166 |  md5_append(&md5state, (const md5_byte_t *)ctx, sizeof(*ctx)); |
| 11167 |  md5_finish(&md5state, ssl_context_id); |
| 11168 | |
| 11169 |  SSL_CTX_set_session_id_context(ctx->ssl_ctx, |
| 11170 |   (const unsigned char *)&ssl_context_id, |
| 11171 |   sizeof(ssl_context_id)); |
| 11172 | |
| 11173 |  if (pem != NULL) { |
| 11174 |    if (!ssl_use_pem_file(ctx, pem)) { |
| 11175 |      return 0; |
| 11176 |    } |
| 11177 |  } |
| 11178 | |
| 11179 |  should_verify_peer = |
| 11180 |   (ctx->config[SSL_DO_VERIFY_PEER] != NULL) |
| 11181 |   && (mg_strcasecmp(ctx->config[SSL_DO_VERIFY_PEER], "yes") == 0); |
| 11182 | |
| 11183 |  use_default_verify_paths = |
| 11184 |   (ctx->config[SSL_DEFAULT_VERIFY_PATHS] != NULL) |
| 11185 |   && (mg_strcasecmp(ctx->config[SSL_DEFAULT_VERIFY_PATHS], "yes") == 0); |
| 11186 | |
| 11187 |  if (should_verify_peer) { |
| 11188 |    ca_path = ctx->config[SSL_CA_PATH]; |
| 11189 |    ca_file = ctx->config[SSL_CA_FILE]; |
| 11190 |    if (SSL_CTX_load_verify_locations(ctx->ssl_ctx, ca_file, ca_path) |
| 11191 |     != 1) { |
| 11192 |      mg_cry(fc(ctx), |
| 11193 |       "SSL_CTX_load_verify_locations error: %s " |
| 11194 |       "ssl_verify_peer requires setting " |
| 11195 |       "either ssl_ca_path or ssl_ca_file. Is any of them " |
| 11196 |       "present in " |
| 11197 |       "the .conf file?", |
| 11198 |       ssl_error()); |
| 11199 |      return 0; |
| 11200 |    } |
| 11201 | |
| 11202 |    SSL_CTX_set_verify(ctx->ssl_ctx, |
| 11203 |     SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, |
| 11204 |     NULL); |
| 11205 | |
| 11206 |    if (use_default_verify_paths |
| 11207 |     && SSL_CTX_set_default_verify_paths(ctx->ssl_ctx) != 1) { |
| 11208 |      mg_cry(fc(ctx), |
| 11209 |       "SSL_CTX_set_default_verify_paths error: %s", |
| 11210 |       ssl_error()); |
| 11211 |      return 0; |
| 11212 |    } |
| 11213 | |
| 11214 |    if (ctx->config[SSL_VERIFY_DEPTH]) { |
| 11215 |      verify_depth = atoi(ctx->config[SSL_VERIFY_DEPTH]); |
| 11216 |      SSL_CTX_set_verify_depth(ctx->ssl_ctx, verify_depth); |
| 11217 |    } |
| 11218 |  } |
| 11219 | |
| 11220 |  if (ctx->config[SSL_CIPHER_LIST] != NULL) { |
| 11221 |    if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, ctx->config[SSL_CIPHER_LIST]) |
| 11222 |     != 1) { |
| 11223 |      mg_cry(fc(ctx), "SSL_CTX_set_cipher_list error: %s", ssl_error()); |
| 11224 |    } |
| 11225 |  } |
| 11226 | |
| 11227 |  return 1; |
| 11228 | } |
| 11229 | |
| 11230 | |
| 11231 | static void |
| 11232 | uninitialize_ssl(struct mg_context *ctx) |
| 11233 | { |
| 11234 |  int i; |
| 11235 |  (void)ctx; |
| 11236 | |
| 11237 |  if (mg_atomic_dec(&cryptolib_users) == 0) { |
| 11238 | |
| 11239 |    /* Shutdown according to |
| 11240 |     * https://wiki.openssl.org/index.php/Library_Initialization#Cleanup |
| 11241 |     * http://stackoverflow.com/questions/29845527/how-to-properly-uninitialize-openssl |
| 11242 |     */ |
| 11243 |    CRYPTO_set_locking_callback(NULL); |
| 11244 |    CRYPTO_set_id_callback(NULL); |
| 11245 |    ENGINE_cleanup(); |
| 11246 |    CONF_modules_unload(1); |
| 11247 |    ERR_free_strings(); |
| 11248 |    EVP_cleanup(); |
| 11249 |    CRYPTO_cleanup_all_ex_data(); |
| 11250 |    ERR_remove_state(0); |
| 11251 | |
| 11252 |    for (i = 0; i < CRYPTO_num_locks(); i++) { |
| 11253 |      pthread_mutex_destroy(&ssl_mutexes[i]); |
| 11254 |    } |
| 11255 |    mg_free(ssl_mutexes); |
| 11256 |    ssl_mutexes = NULL; |
| 11257 |  } |
| 11258 | } |
| 11259 | #endif /* !NO_SSL */ |
| 11260 | |
| 11261 | |
| 11262 | static int |
| 11263 | set_gpass_option(struct mg_context *ctx) |
| 11264 | { |
| 11265 |  if (ctx) { |
| 11266 |    struct file file = STRUCT_FILE_INITIALIZER; |
| 11267 |    const char *path = ctx->config[GLOBAL_PASSWORDS_FILE]; |
| 11268 |    if (path != NULL && !mg_stat(fc(ctx), path, &file)) { |
| 11269 |      mg_cry(fc(ctx), "Cannot open %s: %s", path, strerror(ERRNO)); |
| 11270 |      return 0; |
| 11271 |    } |
| 11272 |    return 1; |
| 11273 |  } |
| 11274 |  return 0; |
| 11275 | } |
| 11276 | |
| 11277 | |
| 11278 | static int |
| 11279 | set_acl_option(struct mg_context *ctx) |
| 11280 | { |
| 11281 |  return check_acl(ctx, (uint32_t)0x7f000001UL) != -1; |
| 11282 | } |
| 11283 | |
| 11284 | |
| 11285 | static void |
| 11286 | reset_per_request_attributes(struct mg_connection *conn) |
| 11287 | { |
| 11288 |  if (!conn) { |
| 11289 |    return; |
| 11290 |  } |
| 11291 |  conn->path_info = NULL; |
| 11292 |  conn->num_bytes_sent = conn->consumed_content = 0; |
| 11293 |  conn->status_code = -1; |
| 11294 |  conn->is_chunked = 0; |
| 11295 |  conn->must_close = conn->request_len = conn->throttle = 0; |
| 11296 |  conn->request_info.content_length = -1; |
| 11297 |  conn->request_info.remote_user = NULL; |
| 11298 |  conn->request_info.request_method = NULL; |
| 11299 |  conn->request_info.request_uri = NULL; |
| 11300 |  conn->request_info.local_uri = NULL; |
| 11301 |  conn->request_info.uri = NULL; /* TODO: cleanup uri, |
| 11302 |   * local_uri and request_uri */ |
| 11303 |  conn->request_info.http_version = NULL; |
| 11304 |  conn->request_info.num_headers = 0; |
| 11305 |  conn->data_len = 0; |
| 11306 |  conn->chunk_remainder = 0; |
| 11307 |  conn->internal_error = 0; |
| 11308 | } |
| 11309 | |
| 11310 | |
| 11311 | static int |
| 11312 | set_sock_timeout(SOCKET sock, int milliseconds) |
| 11313 | { |
| 11314 |  int r0 = 0, r1, r2; |
| 11315 | |
| 11316 | #ifdef _WIN32 |
| 11317 |  /* Windows specific */ |
| 11318 | |
| 11319 |  DWORD tv = (DWORD)milliseconds; |
| 11320 | |
| 11321 | #else |
| 11322 |  /* Linux, ... (not Windows) */ |
| 11323 | |
| 11324 |  struct timeval tv; |
| 11325 | |
| 11326 | /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482): |
| 11327 | * max. time waiting for the acknowledged of TCP data before the connection |
| 11328 | * will be forcefully closed and ETIMEDOUT is returned to the application. |
| 11329 | * If this option is not set, the default timeout of 20-30 minutes is used. |
| 11330 | */ |
| 11331 | /* #define TCP_USER_TIMEOUT (18) */ |
| 11332 | |
| 11333 | #if defined(TCP_USER_TIMEOUT) |
| 11334 |  unsigned int uto = (unsigned int)milliseconds; |
| 11335 |  r0 = setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto)); |
| 11336 | #endif |
| 11337 | |
| 11338 |  memset(&tv, 0, sizeof(tv)); |
| 11339 |  tv.tv_sec = milliseconds / 1000; |
| 11340 |  tv.tv_usec = (milliseconds * 1000) % 1000000; |
| 11341 | |
| 11342 | #endif /* _WIN32 */ |
| 11343 | |
| 11344 |  r1 = setsockopt( |
| 11345 |   sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv)); |
| 11346 |  r2 = setsockopt( |
| 11347 |   sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE)&tv, sizeof(tv)); |
| 11348 | |
| 11349 |  return r0 || r1 || r2; |
| 11350 | } |
| 11351 | |
| 11352 | |
| 11353 | static int |
| 11354 | set_tcp_nodelay(SOCKET sock, int nodelay_on) |
| 11355 | { |
| 11356 |  if (setsockopt(sock, |
| 11357 |   IPPROTO_TCP, |
| 11358 |   TCP_NODELAY, |
| 11359 |   (SOCK_OPT_TYPE)&nodelay_on, |
| 11360 |   sizeof(nodelay_on)) != 0) { |
| 11361 |    /* Error */ |
| 11362 |    return 1; |
| 11363 |  } |
| 11364 |  /* OK */ |
| 11365 |  return 0; |
| 11366 | } |
| 11367 | |
| 11368 | |
| 11369 | static void |
| 11370 | close_socket_gracefully(struct mg_connection *conn) |
| 11371 | { |
| 11372 | #if defined(_WIN32) |
| 11373 |  char buf[MG_BUF_LEN]; |
| 11374 |  int n; |
| 11375 | #endif |
| 11376 |  struct linger linger; |
| 11377 | |
| 11378 |  if (!conn) { |
| 11379 |    return; |
| 11380 |  } |
| 11381 | |
| 11382 |  /* Set linger option to avoid socket hanging out after close. This |
| 11383 |   * prevent |
| 11384 |   * ephemeral port exhaust problem under high QPS. */ |
| 11385 |  linger.l_onoff = 1; |
| 11386 |  linger.l_linger = 1; |
| 11387 | |
| 11388 |  if (setsockopt(conn->client.sock, |
| 11389 |   SOL_SOCKET, |
| 11390 |   SO_LINGER, |
| 11391 |   (char *)&linger, |
| 11392 |   sizeof(linger)) != 0) { |
| 11393 |    mg_cry(conn, |
| 11394 |     "%s: setsockopt(SOL_SOCKET SO_LINGER) failed: %s", |
| 11395 |     __func__, |
| 11396 |     strerror(ERRNO)); |
| 11397 |  } |
| 11398 | |
| 11399 |  /* Send FIN to the client */ |
| 11400 |  shutdown(conn->client.sock, SHUT_WR); |
| 11401 |  set_non_blocking_mode(conn->client.sock); |
| 11402 | |
| 11403 | #if defined(_WIN32) |
| 11404 |  /* Read and discard pending incoming data. If we do not do that and |
| 11405 |   * close |
| 11406 |   * the socket, the data in the send buffer may be discarded. This |
| 11407 |   * behaviour is seen on Windows, when client keeps sending data |
| 11408 |   * when server decides to close the connection; then when client |
| 11409 |   * does recv() it gets no data back. */ |
| 11410 |  do { |
| 11411 |    n = pull( |
| 11412 |     NULL, conn, buf, sizeof(buf), 1E-10 /* TODO: allow 0 as timeout */); |
| 11413 |  } while (n > 0); |
| 11414 | #endif |
| 11415 | |
| 11416 |  /* Now we know that our FIN is ACK-ed, safe to close */ |
| 11417 |  closesocket(conn->client.sock); |
| 11418 |  conn->client.sock = INVALID_SOCKET; |
| 11419 | } |
| 11420 | |
| 11421 | |
| 11422 | static void |
| 11423 | close_connection(struct mg_connection *conn) |
| 11424 | { |
| 11425 |  if (!conn || !conn->ctx) { |
| 11426 |    return; |
| 11427 |  } |
| 11428 | |
| 11429 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 11430 |  if (conn->lua_websocket_state) { |
| 11431 |    lua_websocket_close(conn, conn->lua_websocket_state); |
| 11432 |    conn->lua_websocket_state = NULL; |
| 11433 |  } |
| 11434 | #endif |
| 11435 | |
| 11436 |  /* call the connection_close callback if assigned */ |
| 11437 |  if ((conn->ctx->callbacks.connection_close != NULL) |
| 11438 |   && (conn->ctx->context_type == 1)) { |
| 11439 |    conn->ctx->callbacks.connection_close(conn); |
| 11440 |  } |
| 11441 | |
| 11442 |  mg_lock_connection(conn); |
| 11443 | |
| 11444 |  conn->must_close = 1; |
| 11445 | |
| 11446 | #ifndef NO_SSL |
| 11447 |  if (conn->ssl != NULL) { |
| 11448 |    /* Run SSL_shutdown twice to ensure completly close SSL connection |
| 11449 |     */ |
| 11450 |    SSL_shutdown(conn->ssl); |
| 11451 |    SSL_free(conn->ssl); |
| 11452 |    /* maybe not? CRYPTO_cleanup_all_ex_data(); */ |
| 11453 |    /* see |
| 11454 |     * https://wiki.openssl.org/index.php/Talk:Library_Initialization */ |
| 11455 |    ERR_remove_state(0); |
| 11456 |    conn->ssl = NULL; |
| 11457 |  } |
| 11458 | #endif |
| 11459 |  if (conn->client.sock != INVALID_SOCKET) { |
| 11460 |    close_socket_gracefully(conn); |
| 11461 |    conn->client.sock = INVALID_SOCKET; |
| 11462 |  } |
| 11463 | |
| 11464 |  mg_unlock_connection(conn); |
| 11465 | } |
| 11466 | |
| 11467 | void |
| 11468 | mg_close_connection(struct mg_connection *conn) |
| 11469 | { |
| 11470 |  struct mg_context *client_ctx = NULL; |
| 11471 |  unsigned int i; |
| 11472 | |
| 11473 |  if (conn == NULL) { |
| 11474 |    return; |
| 11475 |  } |
| 11476 | |
| 11477 |  if (conn->ctx->context_type == 2) { |
| 11478 |    client_ctx = conn->ctx; |
| 11479 |    /* client context: loops must end */ |
| 11480 |    conn->ctx->stop_flag = 1; |
| 11481 |  } |
| 11482 | |
| 11483 | #ifndef NO_SSL |
| 11484 |  if (conn->client_ssl_ctx != NULL) { |
| 11485 |    SSL_CTX_free((SSL_CTX *)conn->client_ssl_ctx); |
| 11486 |  } |
| 11487 | #endif |
| 11488 |  close_connection(conn); |
| 11489 |  if (client_ctx != NULL) { |
| 11490 |    /* join worker thread and free context */ |
| 11491 |    for (i = 0; i < client_ctx->cfg_worker_threads; i++) { |
| 11492 |      if (client_ctx->workerthreadids[i] != 0) { |
| 11493 |        mg_join_thread(client_ctx->workerthreadids[i]); |
| 11494 |      } |
| 11495 |    } |
| 11496 |    mg_free(client_ctx->workerthreadids); |
| 11497 |    mg_free(client_ctx); |
| 11498 |    (void)pthread_mutex_destroy(&conn->mutex); |
| 11499 |    mg_free(conn); |
| 11500 |  } |
| 11501 | } |
| 11502 | |
| 11503 | |
| 11504 | static struct mg_connection * |
| 11505 | mg_connect_client_impl(const struct mg_client_options *client_options, |
| 11506 | int use_ssl, |
| 11507 | char *ebuf, |
| 11508 | size_t ebuf_len) |
| 11509 | { |
| 11510 |  static struct mg_context fake_ctx; |
| 11511 |  struct mg_connection *conn = NULL; |
| 11512 |  SOCKET sock; |
| 11513 |  union usa sa; |
| 11514 | |
| 11515 |  if (!connect_socket(&fake_ctx, |
| 11516 |   client_options->host, |
| 11517 |   client_options->port, |
| 11518 |   use_ssl, |
| 11519 |   ebuf, |
| 11520 |   ebuf_len, |
| 11521 |   &sock, |
| 11522 |   &sa)) { |
| 11523 |   |
| 11524 |  } else if ((conn = (struct mg_connection *) |
| 11525 |   mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE)) == NULL) { |
| 11526 |    mg_snprintf(NULL, |
| 11527 |     NULL, /* No truncation check for ebuf */ |
| 11528 |     ebuf, |
| 11529 |     ebuf_len, |
| 11530 |     "calloc(): %s", |
| 11531 |     strerror(ERRNO)); |
| 11532 |    closesocket(sock); |
| 11533 | #ifndef NO_SSL |
| 11534 |  } else if (use_ssl |
| 11535 |   && (conn->client_ssl_ctx = SSL_CTX_new(SSLv23_client_method())) |
| 11536 |   == NULL) { |
| 11537 |    mg_snprintf(NULL, |
| 11538 |     NULL, /* No truncation check for ebuf */ |
| 11539 |     ebuf, |
| 11540 |     ebuf_len, |
| 11541 |     "SSL_CTX_new error"); |
| 11542 |    closesocket(sock); |
| 11543 |    mg_free(conn); |
| 11544 |    conn = NULL; |
| 11545 | #endif /* NO_SSL */ |
| 11546 | |
| 11547 |  } else { |
| 11548 | |
| 11549 | #ifdef USE_IPV6 |
| 11550 |    socklen_t len = (sa.sa.sa_family == AF_INET) |
| 11551 |     ? sizeof(conn->client.rsa.sin) |
| 11552 |     : sizeof(conn->client.rsa.sin6); |
| 11553 |    struct sockaddr *psa = |
| 11554 |     (sa.sa.sa_family == AF_INET) |
| 11555 |     ? (struct sockaddr *)&(conn->client.rsa.sin) |
| 11556 |     : (struct sockaddr *)&(conn->client.rsa.sin6); |
| 11557 | #else |
| 11558 |    socklen_t len = sizeof(conn->client.rsa.sin); |
| 11559 |    struct sockaddr *psa = (struct sockaddr *)&(conn->client.rsa.sin); |
| 11560 | #endif |
| 11561 | |
| 11562 |    conn->buf_size = MAX_REQUEST_SIZE; |
| 11563 |    conn->buf = (char *)(conn + 1); |
| 11564 |    conn->ctx = &fake_ctx; |
| 11565 |    conn->client.sock = sock; |
| 11566 |    conn->client.lsa = sa; |
| 11567 | |
| 11568 |    if (getsockname(sock, psa, &len) != 0) { |
| 11569 |      mg_cry(conn, |
| 11570 |       "%s: getsockname() failed: %s", |
| 11571 |       __func__, |
| 11572 |       strerror(ERRNO)); |
| 11573 |    } |
| 11574 | |
| 11575 |    conn->client.is_ssl = use_ssl ? 1 : 0; |
| 11576 |    (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr); |
| 11577 | |
| 11578 | #ifndef NO_SSL |
| 11579 |    if (use_ssl) { |
| 11580 |      fake_ctx.ssl_ctx = conn->client_ssl_ctx; |
| 11581 | |
| 11582 |      /* TODO: Check ssl_verify_peer and ssl_ca_path here. |
| 11583 |       * SSL_CTX_set_verify call is needed to switch off server |
| 11584 |       * certificate checking, which is off by default in OpenSSL and |
| 11585 |       * on in yaSSL. */ |
| 11586 |      /* TODO: SSL_CTX_set_verify(conn->client_ssl_ctx, |
| 11587 |       * SSL_VERIFY_PEER, verify_ssl_server); */ |
| 11588 | |
| 11589 |      if (client_options->client_cert) { |
| 11590 |        if (!ssl_use_pem_file(&fake_ctx, client_options->client_cert)) { |
| 11591 |          mg_snprintf(NULL, |
| 11592 |           NULL, /* No truncation check for ebuf */ |
| 11593 |           ebuf, |
| 11594 |           ebuf_len, |
| 11595 |           "Can not use SSL client certificate"); |
| 11596 |          SSL_CTX_free(conn->client_ssl_ctx); |
| 11597 |          closesocket(sock); |
| 11598 |          mg_free(conn); |
| 11599 |          conn = NULL; |
| 11600 |        } |
| 11601 |      } |
| 11602 | |
| 11603 |      if (client_options->server_cert) { |
| 11604 |        SSL_CTX_load_verify_locations(conn->client_ssl_ctx, |
| 11605 |         client_options->server_cert, |
| 11606 |         NULL); |
| 11607 |        SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_PEER, NULL); |
| 11608 |      } else { |
| 11609 |        SSL_CTX_set_verify(conn->client_ssl_ctx, SSL_VERIFY_NONE, NULL); |
| 11610 |      } |
| 11611 | |
| 11612 |      if (!sslize(conn, conn->client_ssl_ctx, SSL_connect)) { |
| 11613 |        mg_snprintf(NULL, |
| 11614 |         NULL, /* No truncation check for ebuf */ |
| 11615 |         ebuf, |
| 11616 |         ebuf_len, |
| 11617 |         "SSL connection error"); |
| 11618 |        SSL_CTX_free(conn->client_ssl_ctx); |
| 11619 |        closesocket(sock); |
| 11620 |        mg_free(conn); |
| 11621 |        conn = NULL; |
| 11622 |      } |
| 11623 |    } |
| 11624 | #endif |
| 11625 |  } |
| 11626 | |
| 11627 |  return conn; |
| 11628 | } |
| 11629 | |
| 11630 | |
| 11631 | CIVETWEB_API struct mg_connection * |
| 11632 | mg_connect_client_secure(const struct mg_client_options *client_options, |
| 11633 | char *error_buffer, |
| 11634 | size_t error_buffer_size) |
| 11635 | { |
| 11636 |  return mg_connect_client_impl(client_options, |
| 11637 |   1, |
| 11638 |   error_buffer, |
| 11639 |   error_buffer_size); |
| 11640 | } |
| 11641 | |
| 11642 | |
| 11643 | struct mg_connection * |
| 11644 | mg_connect_client(const char *host, |
| 11645 | int port, |
| 11646 | int use_ssl, |
| 11647 | char *error_buffer, |
| 11648 | size_t error_buffer_size) |
| 11649 | { |
| 11650 |  struct mg_client_options opts; |
| 11651 |  memset(&opts, 0, sizeof(opts)); |
| 11652 |  opts.host = host; |
| 11653 |  opts.port = port; |
| 11654 |  return mg_connect_client_impl(&opts, |
| 11655 |   use_ssl, |
| 11656 |   error_buffer, |
| 11657 |   error_buffer_size); |
| 11658 | } |
| 11659 | |
| 11660 | |
| 11661 | static const struct { |
| 11662 |  const char *proto; |
| 11663 |  size_t proto_len; |
| 11664 |  unsigned default_port; |
| 11665 | } abs_uri_protocols[] = {{"http://", 7, 80}, |
| 11666 | {"https://", 8, 443}, |
| 11667 | {"ws://", 5, 80}, |
| 11668 | {"wss://", 6, 443}, |
| 11669 | {NULL, 0, 0}}; |
| 11670 | |
| 11671 | |
| 11672 | /* Check if the uri is valid. |
| 11673 | * return 0 for invalid uri, |
| 11674 | * return 1 for *, |
| 11675 | * return 2 for relative uri, |
| 11676 | * return 3 for absolute uri without port, |
| 11677 | * return 4 for absolute uri with port */ |
| 11678 | static int |
| 11679 | get_uri_type(const char *uri) |
| 11680 | { |
| 11681 |  int i; |
| 11682 |  char *hostend, *portbegin, *portend; |
| 11683 |  unsigned long port; |
| 11684 | |
| 11685 |  /* According to the HTTP standard |
| 11686 |   * http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 |
| 11687 |   * URI can be an asterisk (*) or should start with slash (relative uri), |
| 11688 |   * or it should start with the protocol (absolute uri). */ |
| 11689 |  if (uri[0] == '*' && uri[1] == '\0') { |
| 11690 |    /* asterisk */ |
| 11691 |    return 1; |
| 11692 |  } |
| 11693 |  if (uri[0] == '/') { |
| 11694 |    /* relative uri */ |
| 11695 |    return 2; |
| 11696 |  } |
| 11697 | |
| 11698 |  /* It could be an absolute uri: */ |
| 11699 |  /* This function only checks if the uri is valid, not if it is |
| 11700 |   * addressing the current server. So civetweb can also be used |
| 11701 |   * as a proxy server. */ |
| 11702 |  for (i = 0; abs_uri_protocols[i].proto != NULL; i++) { |
| 11703 |    if (mg_strncasecmp(uri, |
| 11704 |     abs_uri_protocols[i].proto, |
| 11705 |     abs_uri_protocols[i].proto_len) == 0) { |
| 11706 | |
| 11707 |      hostend = strchr(uri + abs_uri_protocols[i].proto_len, '/'); |
| 11708 |      if (!hostend) { |
| 11709 |        return 0; |
| 11710 |      } |
| 11711 |      portbegin = strchr(uri + abs_uri_protocols[i].proto_len, ':'); |
| 11712 |      if (!portbegin) { |
| 11713 |        return 3; |
| 11714 |      } |
| 11715 | |
| 11716 |      port = strtoul(portbegin + 1, &portend, 10); |
| 11717 |      if ( ) { |
| 11718 |        return 0; |
| 11719 |      } |
| 11720 | |
| 11721 |      return 4; |
| 11722 |    } |
| 11723 |  } |
| 11724 | |
| 11725 |  return 0; |
| 11726 | } |
| 11727 | |
| 11728 | |
| 11729 | /* Return NULL or the relative uri at the current server */ |
| 11730 | static const char * |
| 11731 | get_rel_url_at_current_server(const char *uri, const struct mg_connection *conn) |
| 11732 | { |
| 11733 |  const char *server_domain; |
| 11734 |  size_t server_domain_len; |
| 11735 |  size_t request_domain_len = 0; |
| 11736 |  unsigned long port = 0; |
| 11737 |  int i; |
| 11738 |  const char *hostbegin = NULL; |
| 11739 |  const char *hostend = NULL; |
| 11740 |  const char *portbegin; |
| 11741 |  char *portend; |
| 11742 | |
| 11743 |  /* DNS is case insensitive, so use case insensitive string compare here |
| 11744 |   */ |
| 11745 |  server_domain = conn->ctx->config[AUTHENTICATION_DOMAIN]; |
| 11746 |  if (!server_domain) { |
| 11747 |    return 0; |
| 11748 |  } |
| 11749 |  server_domain_len = strlen(server_domain); |
| 11750 |  if (!server_domain_len) { |
| 11751 |    return 0; |
| 11752 |  } |
| 11753 | |
| 11754 |  for (i = 0; abs_uri_protocols[i].proto != NULL; i++) { |
| 11755 |    if (mg_strncasecmp(uri, |
| 11756 |     abs_uri_protocols[i].proto, |
| 11757 |     abs_uri_protocols[i].proto_len) == 0) { |
| 11758 | |
| 11759 |      hostbegin = uri + abs_uri_protocols[i].proto_len; |
| 11760 |      hostend = strchr(hostbegin, '/'); |
| 11761 |      if (!hostend) { |
| 11762 |        return 0; |
| 11763 |      } |
| 11764 |      portbegin = strchr(hostbegin, ':'); |
| 11765 |      if ((!portbegin) || (portbegin > hostend)) { |
| 11766 |        port = abs_uri_protocols[i].default_port; |
| 11767 |        request_domain_len = (size_t)(hostend - hostbegin); |
| 11768 |      } else { |
| 11769 |        port = strtoul(portbegin + 1, &portend, 10); |
| 11770 |        if ( ) { |
| 11771 |          return 0; |
| 11772 |        } |
| 11773 |        request_domain_len = (size_t)(portbegin - hostbegin); |
| 11774 |      } |
| 11775 |      /* protocol found, port set */ |
| 11776 |      break; |
| 11777 |    } |
| 11778 |  } |
| 11779 | |
| 11780 |  if (!port) { |
| 11781 |    /* port remains 0 if the protocol is not found */ |
| 11782 |    return 0; |
| 11783 |  } |
| 11784 | |
| 11785 | #if defined(USE_IPV6) |
| 11786 |  if (conn->client.lsa.sa.sa_family == AF_INET6) { |
| 11787 |    if (ntohs(conn->client.lsa.sin6.sin6_port) != port) { |
| 11788 |      /* Request is directed to a different port */ |
| 11789 |      return 0; |
| 11790 |    } |
| 11791 |  } else |
| 11792 | #endif |
| 11793 |  { |
| 11794 |    if (ntohs(conn->client.lsa.sin.sin_port) != port) { |
| 11795 |      /* Request is directed to a different port */ |
| 11796 |      return 0; |
| 11797 |    } |
| 11798 |  } |
| 11799 | |
| 11800 |  if ((request_domain_len != server_domain_len) |
| 11801 |   || (0 != memcmp(server_domain, hostbegin, server_domain_len))) { |
| 11802 |    /* Request is directed to another server */ |
| 11803 |    return 0; |
| 11804 |  } |
| 11805 | |
| 11806 |  return hostend; |
| 11807 | } |
| 11808 | |
| 11809 | |
| 11810 | static int |
| 11811 | getreq(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err) |
| 11812 | { |
| 11813 |  const char *cl; |
| 11814 | |
| 11815 |  if (ebuf_len > 0) { |
| 11816 |    ebuf[0] = '\0'; |
| 11817 |  } |
| 11818 |  *err = 0; |
| 11819 | |
| 11820 |  reset_per_request_attributes(conn); |
| 11821 | |
| 11822 |  if (!conn) { |
| 11823 |    mg_snprintf(conn, |
| 11824 |     NULL, /* No truncation check for ebuf */ |
| 11825 |     ebuf, |
| 11826 |     ebuf_len, |
| 11827 |     "%s", |
| 11828 |     "Internal error"); |
| 11829 |    *err = 500; |
| 11830 |    return 0; |
| 11831 |  } |
| 11832 |  /* Set the time the request was received. This value should be used for |
| 11833 |   * timeouts. */ |
| 11834 |  clock_gettime(CLOCK_MONOTONIC, &(conn->req_time)); |
| 11835 | |
| 11836 |  conn->request_len = |
| 11837 |   read_request(NULL, conn, conn->buf, conn->buf_size, &conn->data_len); |
| 11838 |  /* assert(conn->request_len < 0 || conn->data_len >= conn->request_len); |
| 11839 |   */ |
| 11840 |  if (conn->request_len >= 0 && conn->data_len < conn->request_len) { |
| 11841 |    mg_snprintf(conn, |
| 11842 |     NULL, /* No truncation check for ebuf */ |
| 11843 |     ebuf, |
| 11844 |     ebuf_len, |
| 11845 |     "%s", |
| 11846 |     "Invalid request size"); |
| 11847 |    *err = 500; |
| 11848 |    return 0; |
| 11849 |  } |
| 11850 | |
| 11851 |  if (conn->request_len == 0 && conn->data_len == conn->buf_size) { |
| 11852 |    mg_snprintf(conn, |
| 11853 |     NULL, /* No truncation check for ebuf */ |
| 11854 |     ebuf, |
| 11855 |     ebuf_len, |
| 11856 |     "%s", |
| 11857 |     "Request Too Large"); |
| 11858 |    *err = 413; |
| 11859 |    return 0; |
| 11860 |  } else if (conn->request_len <= 0) { |
| 11861 |    if (conn->data_len > 0) { |
| 11862 |      mg_snprintf(conn, |
| 11863 |       NULL, /* No truncation check for ebuf */ |
| 11864 |       ebuf, |
| 11865 |       ebuf_len, |
| 11866 |       "%s", |
| 11867 |       "Client sent malformed request"); |
| 11868 |      *err = 400; |
| 11869 |    } else { |
| 11870 |      /* Server did not send anything -> just close the connection */ |
| 11871 |      conn->must_close = 1; |
| 11872 |      mg_snprintf(conn, |
| 11873 |       NULL, /* No truncation check for ebuf */ |
| 11874 |       ebuf, |
| 11875 |       ebuf_len, |
| 11876 |       "%s", |
| 11877 |       "Client did not send a request"); |
| 11878 |      *err = 0; |
| 11879 |    } |
| 11880 |    return 0; |
| 11881 |  } else if (parse_http_message(conn->buf, |
| 11882 |   conn->buf_size, |
| 11883 |   &conn->request_info) <= 0) { |
| 11884 |    mg_snprintf(conn, |
| 11885 |     NULL, /* No truncation check for ebuf */ |
| 11886 |     ebuf, |
| 11887 |     ebuf_len, |
| 11888 |     "%s", |
| 11889 |     "Bad Request"); |
| 11890 |    *err = 400; |
| 11891 |    return 0; |
| 11892 |  } else { |
| 11893 |    /* Message is a valid request or response */ |
| 11894 |    if ((cl = get_header(&conn->request_info, "Content-Length")) != NULL) { |
| 11895 |      /* Request/response has content length set */ |
| 11896 |      char *endptr = NULL; |
| 11897 |      conn->content_len = strtoll(cl, &endptr, 10); |
| 11898 |      if (endptr == cl) { |
| 11899 |        mg_snprintf(conn, |
| 11900 |         NULL, /* No truncation check for ebuf */ |
| 11901 |         ebuf, |
| 11902 |         ebuf_len, |
| 11903 |         "%s", |
| 11904 |         "Bad Request"); |
| 11905 |        *err = 411; |
| 11906 |        return 0; |
| 11907 |      } |
| 11908 |      /* Publish the content length back to the request info. */ |
| 11909 |      conn->request_info.content_length = conn->content_len; |
| 11910 |    } else if ((cl = get_header(&conn->request_info, "Transfer-Encoding")) |
| 11911 |     != NULL |
| 11912 |     && !mg_strcasecmp(cl, "chunked")) { |
| 11913 |      conn->is_chunked = 1; |
| 11914 |    } else if (!mg_strcasecmp(conn->request_info.request_method, "POST") |
| 11915 |     || !mg_strcasecmp(conn->request_info.request_method, |
| 11916 |     "PUT")) { |
| 11917 |      /* POST or PUT request without content length set */ |
| 11918 |      conn->content_len = -1; |
| 11919 |    } else if (!mg_strncasecmp(conn->request_info.request_method, |
| 11920 |     "HTTP/", |
| 11921 |     5)) { |
| 11922 |      /* Response without content length set */ |
| 11923 |      conn->content_len = -1; |
| 11924 |    } else { |
| 11925 |      /* Other request */ |
| 11926 |      conn->content_len = 0; |
| 11927 |    } |
| 11928 |  } |
| 11929 |  return 1; |
| 11930 | } |
| 11931 | |
| 11932 | |
| 11933 | int |
| 11934 | mg_get_response(struct mg_connection *conn, |
| 11935 | char *ebuf, |
| 11936 | size_t ebuf_len, |
| 11937 | int timeout) |
| 11938 | { |
| 11939 |  if (conn) { |
| 11940 |    /* Implementation of API function for HTTP clients */ |
| 11941 |    int err, ret; |
| 11942 |    struct mg_context *octx = conn->ctx; |
| 11943 |    struct mg_context rctx = *(conn->ctx); |
| 11944 |    char txt[32]; /* will not overflow */ |
| 11945 | |
| 11946 |    if (timeout >= 0) { |
| 11947 |      mg_snprintf(conn, NULL, txt, sizeof(txt), "%i", timeout); |
| 11948 |      rctx.config[REQUEST_TIMEOUT] = txt; |
| 11949 |      set_sock_timeout(conn->client.sock, timeout); |
| 11950 |    } else { |
| 11951 |      rctx.config[REQUEST_TIMEOUT] = NULL; |
| 11952 |    } |
| 11953 | |
| 11954 |    conn->ctx = &rctx; |
| 11955 |    ret = getreq(conn, ebuf, ebuf_len, &err); |
| 11956 |    conn->ctx = octx; |
| 11957 | |
| 11958 |    /* TODO: 1) uri is deprecated; |
| 11959 |     * 2) here, ri.uri is the http response code */ |
| 11960 |    conn->request_info.uri = conn->request_info.request_uri; |
| 11961 | |
| 11962 |    /* TODO (mid): Define proper return values - maybe return length? |
| 11963 |     * For the first test use <0 for error and >0 for OK */ |
| 11964 |    return (ret == 0) ? -1 : +1; |
| 11965 |  } |
| 11966 |  return -1; |
| 11967 | } |
| 11968 | |
| 11969 | |
| 11970 | struct mg_connection * |
| 11971 | mg_download(const char *host, |
| 11972 | int port, |
| 11973 | int use_ssl, |
| 11974 | char *ebuf, |
| 11975 | size_t ebuf_len, |
| 11976 | const char *fmt, |
| 11977 | ...) |
| 11978 | { |
| 11979 |  struct mg_connection *conn; |
| 11980 |  va_list ap; |
| 11981 |  int i; |
| 11982 |  int reqerr; |
| 11983 | |
| 11984 |  va_start(ap, fmt); |
| 11985 |  ebuf[0] = '\0'; |
| 11986 | |
| 11987 |  /* open a connection */ |
| 11988 |  conn = mg_connect_client(host, port, use_ssl, ebuf, ebuf_len); |
| 11989 | |
| 11990 |  if (conn != NULL) { |
| 11991 |    i = mg_vprintf(conn, fmt, ap); |
| 11992 |    if (i <= 0) { |
| 11993 |      mg_snprintf(conn, |
| 11994 |       NULL, /* No truncation check for ebuf */ |
| 11995 |       ebuf, |
| 11996 |       ebuf_len, |
| 11997 |       "%s", |
| 11998 |       "Error sending request"); |
| 11999 |    } else { |
| 12000 |      getreq(conn, ebuf, ebuf_len, &reqerr); |
| 12001 | |
| 12002 |      /* TODO: 1) uri is deprecated; |
| 12003 |       * 2) here, ri.uri is the http response code */ |
| 12004 |      conn->request_info.uri = conn->request_info.request_uri; |
| 12005 |    } |
| 12006 |  } |
| 12007 | |
| 12008 |  /* if an error occured, close the connection */ |
| 12009 |  if (ebuf[0] != '\0' && conn != NULL) { |
| 12010 |    mg_close_connection(conn); |
| 12011 |    conn = NULL; |
| 12012 |  } |
| 12013 | |
| 12014 |  va_end(ap); |
| 12015 |  return conn; |
| 12016 | } |
| 12017 | |
| 12018 | |
| 12019 | struct websocket_client_thread_data { |
| 12020 |  struct mg_connection *conn; |
| 12021 |  mg_websocket_data_handler data_handler; |
| 12022 |  mg_websocket_close_handler close_handler; |
| 12023 |  void *callback_data; |
| 12024 | }; |
| 12025 | |
| 12026 | |
| 12027 | #if defined(USE_WEBSOCKET) |
| 12028 | #ifdef _WIN32 |
| 12029 | static unsigned __stdcall websocket_client_thread(void *data) |
| 12030 | #else |
| 12031 | static void * |
| 12032 | websocket_client_thread(void *data) |
| 12033 | #endif |
| 12034 | { |
| 12035 |  struct websocket_client_thread_data *cdata = |
| 12036 |   (struct websocket_client_thread_data *)data; |
| 12037 | |
| 12038 |  mg_set_thread_name("ws-client"); |
| 12039 | |
| 12040 |  if (cdata->conn->ctx) { |
| 12041 |    if (cdata->conn->ctx->callbacks.init_thread) { |
| 12042 |      /* 3 indicates a websocket client thread */ |
| 12043 |      /* TODO: check if conn->ctx can be set */ |
| 12044 |      cdata->conn->ctx->callbacks.init_thread(cdata->conn->ctx, 3); |
| 12045 |    } |
| 12046 |  } |
| 12047 | |
| 12048 |  read_websocket(cdata->conn, cdata->data_handler, cdata->callback_data); |
| 12049 | |
| 12050 |  DEBUG_TRACE("%s", "Websocket client thread exited\n"); |
| 12051 | |
| 12052 |  if (cdata->close_handler != NULL) { |
| 12053 |    cdata->close_handler(cdata->conn, cdata->callback_data); |
| 12054 |  } |
| 12055 | |
| 12056 |  mg_free((void *)cdata); |
| 12057 | |
| 12058 | #ifdef _WIN32 |
| 12059 |  return 0; |
| 12060 | #else |
| 12061 |  return NULL; |
| 12062 | #endif |
| 12063 | } |
| 12064 | #endif |
| 12065 | |
| 12066 | |
| 12067 | struct mg_connection * |
| 12068 | mg_connect_websocket_client(const char *host, |
| 12069 | int port, |
| 12070 | int use_ssl, |
| 12071 | char *error_buffer, |
| 12072 | size_t error_buffer_size, |
| 12073 | const char *path, |
| 12074 | const char *origin, |
| 12075 | mg_websocket_data_handler data_func, |
| 12076 | mg_websocket_close_handler close_func, |
| 12077 | void *user_data) |
| 12078 | { |
| 12079 |  struct mg_connection *conn = NULL; |
| 12080 | |
| 12081 | #if defined(USE_WEBSOCKET) |
| 12082 |  struct mg_context *newctx = NULL; |
| 12083 |  struct websocket_client_thread_data *thread_data; |
| 12084 |  static const char *magic = "x3JJHMbDL1EzLkh9GBhXDw=="; |
| 12085 |  static const char *handshake_req; |
| 12086 | |
| 12087 |  if (origin != NULL) { |
| 12088 |    handshake_req = "GET %s HTTP/1.1\r\n" |
| 12089 |     "Host: %s\r\n" |
| 12090 |     "Upgrade: websocket\r\n" |
| 12091 |     "Connection: Upgrade\r\n" |
| 12092 |     "Sec-WebSocket-Key: %s\r\n" |
| 12093 |     "Sec-WebSocket-Version: 13\r\n" |
| 12094 |     "Origin: %s\r\n" |
| 12095 |     "\r\n"; |
| 12096 |  } else { |
| 12097 |    handshake_req = "GET %s HTTP/1.1\r\n" |
| 12098 |     "Host: %s\r\n" |
| 12099 |     "Upgrade: websocket\r\n" |
| 12100 |     "Connection: Upgrade\r\n" |
| 12101 |     "Sec-WebSocket-Key: %s\r\n" |
| 12102 |     "Sec-WebSocket-Version: 13\r\n" |
| 12103 |     "\r\n"; |
| 12104 |  } |
| 12105 | |
| 12106 |  /* Establish the client connection and request upgrade */ |
| 12107 |  conn = mg_download(host, |
| 12108 |   port, |
| 12109 |   use_ssl, |
| 12110 |   error_buffer, |
| 12111 |   error_buffer_size, |
| 12112 |   handshake_req, |
| 12113 |   path, |
| 12114 |   host, |
| 12115 |   magic, |
| 12116 |   origin); |
| 12117 | |
| 12118 |  /* Connection object will be null if something goes wrong */ |
| 12119 |  if (conn == NULL || (strcmp(conn->request_info.request_uri, "101") != 0)) { |
| 12120 |    if (!*error_buffer) { |
| 12121 |      /* if there is a connection, but it did not return 101, |
| 12122 |       * error_buffer is not yet set */ |
| 12123 |      mg_snprintf(conn, |
| 12124 |       NULL, /* No truncation check for ebuf */ |
| 12125 |       error_buffer, |
| 12126 |       error_buffer_size, |
| 12127 |       "Unexpected server reply"); |
| 12128 |    } |
| 12129 |    DEBUG_TRACE("Websocket client connect error: %s\r\n", error_buffer); |
| 12130 |    if (conn != NULL) { |
| 12131 |      mg_free(conn); |
| 12132 |      conn = NULL; |
| 12133 |    } |
| 12134 |    return conn; |
| 12135 |  } |
| 12136 | |
| 12137 |  /* For client connections, mg_context is fake. Since we need to set a |
| 12138 |   * callback function, we need to create a copy and modify it. */ |
| 12139 |  newctx = (struct mg_context *)mg_malloc(sizeof(struct mg_context)); |
| 12140 |  memcpy(newctx, conn->ctx, sizeof(struct mg_context)); |
| 12141 |  newctx->user_data = user_data; |
| 12142 |  newctx->context_type = 2; /* client context type */ |
| 12143 |  newctx->cfg_worker_threads = 1; /* one worker thread will be created */ |
| 12144 |  newctx->workerthreadids = |
| 12145 |   (pthread_t *)mg_calloc(newctx->cfg_worker_threads, sizeof(pthread_t)); |
| 12146 |  conn->ctx = newctx; |
| 12147 |  thread_data = (struct websocket_client_thread_data *) |
| 12148 |   mg_calloc(sizeof(struct websocket_client_thread_data), 1); |
| 12149 |  thread_data->conn = conn; |
| 12150 |  thread_data->data_handler = data_func; |
| 12151 |  thread_data->close_handler = close_func; |
| 12152 |  thread_data->callback_data = NULL; |
| 12153 | |
| 12154 |  /* Start a thread to read the websocket client connection |
| 12155 |   * This thread will automatically stop when mg_disconnect is |
| 12156 |   * called on the client connection */ |
| 12157 |  if (mg_start_thread_with_id(websocket_client_thread, |
| 12158 |   (void *)thread_data, |
| 12159 |   newctx->workerthreadids) != 0) { |
| 12160 |    mg_free((void *)thread_data); |
| 12161 |    mg_free((void *)newctx->workerthreadids); |
| 12162 |    mg_free((void *)newctx); |
| 12163 |    mg_free((void *)conn); |
| 12164 |    conn = NULL; |
| 12165 |    DEBUG_TRACE("%s", |
| 12166 |     "Websocket client connect thread could not be started\r\n"); |
| 12167 |  } |
| 12168 | #else |
| 12169 |  /* Appease "unused parameter" warnings */ |
| 12170 |  (void)host; |
| 12171 |  (void)port; |
| 12172 |  (void)use_ssl; |
| 12173 |  (void)error_buffer; |
| 12174 |  (void)error_buffer_size; |
| 12175 |  (void)path; |
| 12176 |  (void)origin; |
| 12177 |  (void)user_data; |
| 12178 |  (void)data_func; |
| 12179 |  (void)close_func; |
| 12180 | #endif |
| 12181 | |
| 12182 |  return conn; |
| 12183 | } |
| 12184 | |
| 12185 | |
| 12186 | static void |
| 12187 | process_new_connection(struct mg_connection *conn) |
| 12188 | { |
| 12189 |  if (conn && conn->ctx) { |
| 12190 |    struct mg_request_info *ri = &conn->request_info; |
| 12191 |    int keep_alive_enabled, keep_alive, discard_len; |
| 12192 |    char ebuf[100]; |
| 12193 |    const char *hostend; |
| 12194 |    int reqerr, uri_type; |
| 12195 | |
| 12196 |    keep_alive_enabled = |
| 12197 |     !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes"); |
| 12198 | |
| 12199 |    /* Important: on new connection, reset the receiving buffer. Credit |
| 12200 |     * goes to crule42. */ |
| 12201 |    conn->data_len = 0; |
| 12202 |    do { |
| 12203 |      if (!getreq(conn, ebuf, sizeof(ebuf), &reqerr)) { |
| 12204 |        /* The request sent by the client could not be understood by |
| 12205 |         * the server, or it was incomplete or a timeout. Send an |
| 12206 |         * error message and close the connection. */ |
| 12207 |        if (reqerr > 0) { |
| 12208 |          /*assert(ebuf[0] != '\0');*/ |
| 12209 |          send_http_error(conn, reqerr, "%s", ebuf); |
| 12210 |        } |
| 12211 |      } else if (strcmp(ri->http_version, "1.0") |
| 12212 |       && strcmp(ri->http_version, "1.1")) { |
| 12213 |        mg_snprintf(conn, |
| 12214 |         NULL, /* No truncation check for ebuf */ |
| 12215 |         ebuf, |
| 12216 |         sizeof(ebuf), |
| 12217 |         "Bad HTTP version: [%s]", |
| 12218 |         ri->http_version); |
| 12219 |        send_http_error(conn, 505, "%s", ebuf); |
| 12220 |      } |
| 12221 | |
| 12222 |      if (ebuf[0] == '\0') { |
| 12223 |        uri_type = get_uri_type(conn->request_info.request_uri); |
| 12224 |        switch (uri_type) { |
| 12225 |        case 1: |
| 12226 |          /* Asterisk */ |
| 12227 |          conn->request_info.local_uri = NULL; |
| 12228 |          break; |
| 12229 |        case 2: |
| 12230 |          /* relative uri */ |
| 12231 |          conn->request_info.local_uri = |
| 12232 |           conn->request_info.request_uri; |
| 12233 |          break; |
| 12234 |        case 3: |
| 12235 |        case 4: |
| 12236 |          /* absolute uri (with/without port) */ |
| 12237 |          hostend = get_rel_url_at_current_server( |
| 12238 |           conn->request_info.request_uri, conn); |
| 12239 |          if (hostend) { |
| 12240 |            conn->request_info.local_uri = hostend; |
| 12241 |          } else { |
| 12242 |            conn->request_info.local_uri = NULL; |
| 12243 |          } |
| 12244 |          break; |
| 12245 |        default: |
| 12246 |          mg_snprintf(conn, |
| 12247 |           NULL, /* No truncation check for ebuf */ |
| 12248 |           ebuf, |
| 12249 |           sizeof(ebuf), |
| 12250 |           "Invalid URI: [%s]", |
| 12251 |           ri->request_uri); |
| 12252 |          send_http_error(conn, 400, "%s", ebuf); |
| 12253 |          break; |
| 12254 |        } |
| 12255 | |
| 12256 |        /* TODO: cleanup uri, local_uri and request_uri */ |
| 12257 |        conn->request_info.uri = conn->request_info.local_uri; |
| 12258 |      } |
| 12259 | |
| 12260 |      if (ebuf[0] == '\0') { |
| 12261 |        if (conn->request_info.local_uri) { |
| 12262 |          /* handle request to local server */ |
| 12263 |          handle_request(conn); |
| 12264 |          if (conn->ctx->callbacks.end_request != NULL) { |
| 12265 |            conn->ctx->callbacks.end_request(conn, |
| 12266 |             conn->status_code); |
| 12267 |          } |
| 12268 |          log_access(conn); |
| 12269 |        } else { |
| 12270 |          /* TODO: handle non-local request (PROXY) */ |
| 12271 |          conn->must_close = 1; |
| 12272 |        } |
| 12273 |      } else { |
| 12274 |        conn->must_close = 1; |
| 12275 |      } |
| 12276 | |
| 12277 |      if (ri->remote_user != NULL) { |
| 12278 |        mg_free((void *)ri->remote_user); |
| 12279 |        /* Important! When having connections with and without auth |
| 12280 |         * would cause double free and then crash */ |
| 12281 |        ri->remote_user = NULL; |
| 12282 |      } |
| 12283 | |
| 12284 |      /* NOTE(lsm): order is important here. should_keep_alive() call |
| 12285 |       * is |
| 12286 |       * using parsed request, which will be invalid after memmove's |
| 12287 |       * below. |
| 12288 |       * Therefore, memorize should_keep_alive() result now for later |
| 12289 |       * use |
| 12290 |       * in loop exit condition. */ |
| 12291 |      keep_alive = conn->ctx->stop_flag == 0 && keep_alive_enabled |
| 12292 |       && conn->content_len >= 0 && should_keep_alive(conn); |
| 12293 | |
| 12294 |      /* Discard all buffered data for this request */ |
| 12295 |      discard_len = conn->content_len >= 0 && conn->request_len > 0 |
| 12296 |       && conn->request_len + conn->content_len |
| 12297 |       < (int64_t)conn->data_len |
| 12298 |       ? (int)(conn->request_len + conn->content_len) |
| 12299 |       : conn->data_len; |
| 12300 |      /*assert(discard_len >= 0);*/ |
| 12301 |      if (discard_len < 0) |
| 12302 |        break; |
| 12303 |      conn->data_len -= discard_len; |
| 12304 |      if (conn->data_len > 0) { |
| 12305 |        memmove(conn->buf, |
| 12306 |         conn->buf + discard_len, |
| 12307 |         (size_t)conn->data_len); |
| 12308 |      } |
| 12309 | |
| 12310 |      /* assert(conn->data_len >= 0); */ |
| 12311 |      /* assert(conn->data_len <= conn->buf_size); */ |
| 12312 | |
| 12313 |      if ((conn->data_len < 0) || (conn->data_len > conn->buf_size)) { |
| 12314 |        break; |
| 12315 |      } |
| 12316 | |
| 12317 |    } while (keep_alive); |
| 12318 |  } |
| 12319 | } |
| 12320 | |
| 12321 | |
| 12322 | /* Worker threads take accepted socket from the queue */ |
| 12323 | static int |
| 12324 | consume_socket(struct mg_context *ctx, struct socket *sp) |
| 12325 | { |
| 12326 | #define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue))) |
| 12327 |  if (!ctx) { |
| 12328 |    return 0; |
| 12329 |  } |
| 12330 | |
| 12331 |  (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 12332 |  DEBUG_TRACE("%s", "going idle"); |
| 12333 | |
| 12334 |  /* If the queue is empty, wait. We're idle at this point. */ |
| 12335 |  while (ctx->sq_head == ctx->sq_tail && ctx->stop_flag == 0) { |
| 12336 |    pthread_cond_wait(&ctx->sq_full, &ctx->thread_mutex); |
| 12337 |  } |
| 12338 | |
| 12339 |  /* If we're stopping, sq_head may be equal to sq_tail. */ |
| 12340 |  if (ctx->sq_head > ctx->sq_tail) { |
| 12341 |    /* Copy socket from the queue and increment tail */ |
| 12342 |    *sp = ctx->queue[ctx->sq_tail % QUEUE_SIZE(ctx)]; |
| 12343 |    ctx->sq_tail++; |
| 12344 | |
| 12345 |    DEBUG_TRACE("grabbed socket %d, going busy", sp ? sp->sock : -1); |
| 12346 | |
| 12347 |    /* Wrap pointers if needed */ |
| 12348 |    while (ctx->sq_tail > QUEUE_SIZE(ctx)) { |
| 12349 |      ctx->sq_tail -= QUEUE_SIZE(ctx); |
| 12350 |      ctx->sq_head -= QUEUE_SIZE(ctx); |
| 12351 |    } |
| 12352 |  } |
| 12353 | |
| 12354 |  (void)pthread_cond_signal(&ctx->sq_empty); |
| 12355 |  (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 12356 | |
| 12357 |  return !ctx->stop_flag; |
| 12358 | #undef QUEUE_SIZE |
| 12359 | } |
| 12360 | |
| 12361 | |
| 12362 | static void * |
| 12363 | worker_thread_run(void *thread_func_param) |
| 12364 | { |
| 12365 |  struct mg_context *ctx = (struct mg_context *)thread_func_param; |
| 12366 |  struct mg_connection *conn; |
| 12367 |  struct mg_workerTLS tls; |
| 12368 | #if defined(MG_LEGACY_INTERFACE) |
| 12369 |  uint32_t addr; |
| 12370 | #endif |
| 12371 | |
| 12372 |  mg_set_thread_name("worker"); |
| 12373 | |
| 12374 |  tls.is_master = 0; |
| 12375 |  tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); |
| 12376 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12377 |  tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 12378 | #endif |
| 12379 | |
| 12380 |  if (ctx->callbacks.init_thread) { |
| 12381 |    /* call init_thread for a worker thread (type 1) */ |
| 12382 |    ctx->callbacks.init_thread(ctx, 1); |
| 12383 |  } |
| 12384 | |
| 12385 |  conn = |
| 12386 |   (struct mg_connection *)mg_calloc(1, sizeof(*conn) + MAX_REQUEST_SIZE); |
| 12387 |  if (conn == NULL) { |
| 12388 |    mg_cry(fc(ctx), "%s", "Cannot create new connection struct, OOM"); |
| 12389 |  } else { |
| 12390 |    pthread_setspecific(sTlsKey, &tls); |
| 12391 |    conn->buf_size = MAX_REQUEST_SIZE; |
| 12392 |    conn->buf = (char *)(conn + 1); |
| 12393 |    conn->ctx = ctx; |
| 12394 |    conn->request_info.user_data = ctx->user_data; |
| 12395 |    /* Allocate a mutex for this connection to allow communication both |
| 12396 |     * within the request handler and from elsewhere in the application |
| 12397 |     */ |
| 12398 |    (void)pthread_mutex_init(&conn->mutex, &pthread_mutex_attr); |
| 12399 | |
| 12400 |    /* Call consume_socket() even when ctx->stop_flag > 0, to let it |
| 12401 |     * signal sq_empty condvar to wake up the master waiting in |
| 12402 |     * produce_socket() */ |
| 12403 |    while (consume_socket(ctx, &conn->client)) { |
| 12404 |      conn->conn_birth_time = time(NULL); |
| 12405 | |
| 12406 | /* Fill in IP, port info early so even if SSL setup below fails, |
| 12407 | * error handler would have the corresponding info. |
| 12408 | * Thanks to Johannes Winkelmann for the patch. |
| 12409 | */ |
| 12410 | #if defined(USE_IPV6) |
| 12411 |      if (conn->client.rsa.sa.sa_family == AF_INET6) { |
| 12412 |        conn->request_info.remote_port = |
| 12413 |         ntohs(conn->client.rsa.sin6.sin6_port); |
| 12414 |      } else |
| 12415 | #endif |
| 12416 |      { |
| 12417 |        conn->request_info.remote_port = |
| 12418 |         ntohs(conn->client.rsa.sin.sin_port); |
| 12419 |      } |
| 12420 | |
| 12421 |      sockaddr_to_string(conn->request_info.remote_addr, |
| 12422 |       sizeof(conn->request_info.remote_addr), |
| 12423 |       &conn->client.rsa); |
| 12424 | |
| 12425 | #if defined(MG_LEGACY_INTERFACE) |
| 12426 |      /* This legacy interface only works for the IPv4 case */ |
| 12427 |      addr = ntohl(conn->client.rsa.sin.sin_addr.s_addr); |
| 12428 |      memcpy(&conn->request_info.remote_ip, &addr, 4); |
| 12429 | #endif |
| 12430 | |
| 12431 |      conn->request_info.is_ssl = conn->client.is_ssl; |
| 12432 | |
| 12433 |      if (!conn->client.is_ssl |
| 12434 | #ifndef NO_SSL |
| 12435 |       || sslize(conn, conn->ctx->ssl_ctx, SSL_accept) |
| 12436 | #endif |
| 12437 |       ) { |
| 12438 | |
| 12439 | |
| 12440 |        process_new_connection(conn); |
| 12441 |      } |
| 12442 | |
| 12443 |      close_connection(conn); |
| 12444 |    } |
| 12445 |  } |
| 12446 | |
| 12447 |  /* Signal master that we're done with connection and exiting */ |
| 12448 |  (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 12449 |  ctx->running_worker_threads--; |
| 12450 |  (void)pthread_cond_signal(&ctx->thread_cond); |
| 12451 |  /* assert(ctx->running_worker_threads >= 0); */ |
| 12452 |  (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 12453 | |
| 12454 |  pthread_setspecific(sTlsKey, NULL); |
| 12455 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12456 |  CloseHandle(tls.pthread_cond_helper_mutex); |
| 12457 | #endif |
| 12458 |  pthread_mutex_destroy(&conn->mutex); |
| 12459 |  mg_free(conn); |
| 12460 | |
| 12461 |  DEBUG_TRACE("%s", "exiting"); |
| 12462 |  return NULL; |
| 12463 | } |
| 12464 | |
| 12465 | |
| 12466 | /* Threads have different return types on Windows and Unix. */ |
| 12467 | #ifdef _WIN32 |
| 12468 | static unsigned __stdcall worker_thread(void *thread_func_param) |
| 12469 | { |
| 12470 |  worker_thread_run(thread_func_param); |
| 12471 |  return 0; |
| 12472 | } |
| 12473 | #else |
| 12474 | static void * |
| 12475 | worker_thread(void *thread_func_param) |
| 12476 | { |
| 12477 |  worker_thread_run(thread_func_param); |
| 12478 |  return NULL; |
| 12479 | } |
| 12480 | #endif /* _WIN32 */ |
| 12481 | |
| 12482 | |
| 12483 | /* Master thread adds accepted socket to a queue */ |
| 12484 | static void |
| 12485 | produce_socket(struct mg_context *ctx, const struct socket *sp) |
| 12486 | { |
| 12487 | #define QUEUE_SIZE(ctx) ((int)(ARRAY_SIZE(ctx->queue))) |
| 12488 |  if (!ctx) { |
| 12489 |    return; |
| 12490 |  } |
| 12491 |  (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 12492 | |
| 12493 |  /* If the queue is full, wait */ |
| 12494 |  while (ctx->stop_flag == 0 |
| 12495 |   && ctx->sq_head - ctx->sq_tail >= QUEUE_SIZE(ctx)) { |
| 12496 |    (void)pthread_cond_wait(&ctx->sq_empty, &ctx->thread_mutex); |
| 12497 |  } |
| 12498 | |
| 12499 |  if (ctx->sq_head - ctx->sq_tail < QUEUE_SIZE(ctx)) { |
| 12500 |    /* Copy socket to the queue and increment head */ |
| 12501 |    ctx->queue[ctx->sq_head % QUEUE_SIZE(ctx)] = *sp; |
| 12502 |    ctx->sq_head++; |
| 12503 |    DEBUG_TRACE("queued socket %d", sp ? sp->sock : -1); |
| 12504 |  } |
| 12505 | |
| 12506 |  (void)pthread_cond_signal(&ctx->sq_full); |
| 12507 |  (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 12508 | #undef QUEUE_SIZE |
| 12509 | } |
| 12510 | |
| 12511 | |
| 12512 | static void |
| 12513 | accept_new_connection(const struct socket *listener, struct mg_context *ctx) |
| 12514 | { |
| 12515 |  struct socket so; |
| 12516 |  char src_addr[IP_ADDR_STR_LEN]; |
| 12517 |  socklen_t len = sizeof(so.rsa); |
| 12518 |  int on = 1; |
| 12519 |  int timeout; |
| 12520 | |
| 12521 |  if (!listener) { |
| 12522 |    return; |
| 12523 |  } |
| 12524 | |
| 12525 |  if ((so.sock = accept(listener->sock, &so.rsa.sa, &len)) |
| 12526 |   == INVALID_SOCKET) { |
| 12527 |  } else if (!check_acl(ctx, ntohl(*(uint32_t *)&so.rsa.sin.sin_addr))) { |
| 12528 |    sockaddr_to_string(src_addr, sizeof(src_addr), &so.rsa); |
| 12529 |    mg_cry(fc(ctx), "%s: %s is not allowed to connect", __func__, src_addr); |
| 12530 |    closesocket(so.sock); |
| 12531 |    so.sock = INVALID_SOCKET; |
| 12532 |  } else { |
| 12533 |    /* Put so socket structure into the queue */ |
| 12534 |    DEBUG_TRACE("Accepted socket %d", (int)so.sock); |
| 12535 |    set_close_on_exec(so.sock, fc(ctx)); |
| 12536 |    so.is_ssl = listener->is_ssl; |
| 12537 |    so.ssl_redir = listener->ssl_redir; |
| 12538 |    if (getsockname(so.sock, &so.lsa.sa, &len) != 0) { |
| 12539 |      mg_cry(fc(ctx), |
| 12540 |       "%s: getsockname() failed: %s", |
| 12541 |       __func__, |
| 12542 |       strerror(ERRNO)); |
| 12543 |    } |
| 12544 | |
| 12545 |    /* Set TCP keep-alive. This is needed because if HTTP-level |
| 12546 |     * keep-alive |
| 12547 |     * is enabled, and client resets the connection, server won't get |
| 12548 |     * TCP FIN or RST and will keep the connection open forever. With |
| 12549 |     * TCP keep-alive, next keep-alive handshake will figure out that |
| 12550 |     * the client is down and will close the server end. |
| 12551 |     * Thanks to Igor Klopov who suggested the patch. */ |
| 12552 |    if (setsockopt(so.sock, |
| 12553 |     SOL_SOCKET, |
| 12554 |     SO_KEEPALIVE, |
| 12555 |     (SOCK_OPT_TYPE)&on, |
| 12556 |     sizeof(on)) != 0) { |
| 12557 |      mg_cry(fc(ctx), |
| 12558 |       "%s: setsockopt(SOL_SOCKET SO_KEEPALIVE) failed: %s", |
| 12559 |       __func__, |
| 12560 |       strerror(ERRNO)); |
| 12561 |    } |
| 12562 | |
| 12563 | |
| 12564 |    /* Disable TCP Nagle's algorithm. Normally TCP packets are |
| 12565 |     * coalesced |
| 12566 |     * to effectively fill up the underlying IP packet payload and |
| 12567 |     * reduce |
| 12568 |     * the overhead of sending lots of small buffers. However this hurts |
| 12569 |     * the server's throughput (ie. operations per second) when HTTP 1.1 |
| 12570 |     * persistent connections are used and the responses are relatively |
| 12571 |     * small (eg. less than 1400 bytes). |
| 12572 |     */ |
| 12573 |    if ((ctx != NULL) && (ctx->config[CONFIG_TCP_NODELAY] != NULL) |
| 12574 |     && (!strcmp(ctx->config[CONFIG_TCP_NODELAY], "1"))) { |
| 12575 |      if (set_tcp_nodelay(so.sock, 1) != 0) { |
| 12576 |        mg_cry(fc(ctx), |
| 12577 |         "%s: setsockopt(IPPROTO_TCP TCP_NODELAY) failed: %s", |
| 12578 |         __func__, |
| 12579 |         strerror(ERRNO)); |
| 12580 |      } |
| 12581 |    } |
| 12582 | |
| 12583 |    if (ctx && ctx->config[REQUEST_TIMEOUT]) { |
| 12584 |      timeout = atoi(ctx->config[REQUEST_TIMEOUT]); |
| 12585 |    } else { |
| 12586 |      timeout = -1; |
| 12587 |    } |
| 12588 | |
| 12589 |    /* Set socket timeout to the given value, but not more than a |
| 12590 |     * a certain limit (SOCKET_TIMEOUT_QUANTUM, default 10 seconds), |
| 12591 |     * so the server can exit after that time if requested. */ |
| 12592 |    if ((timeout > 0) && (timeout < SOCKET_TIMEOUT_QUANTUM)) { |
| 12593 |      set_sock_timeout(so.sock, timeout); |
| 12594 |    } else { |
| 12595 |      set_sock_timeout(so.sock, SOCKET_TIMEOUT_QUANTUM); |
| 12596 |    } |
| 12597 | |
| 12598 |    produce_socket(ctx, &so); |
| 12599 |  } |
| 12600 | } |
| 12601 | |
| 12602 | |
| 12603 | static void |
| 12604 | master_thread_run(void *thread_func_param) |
| 12605 | { |
| 12606 |  struct mg_context *ctx = (struct mg_context *)thread_func_param; |
| 12607 |  struct mg_workerTLS tls; |
| 12608 |  struct pollfd *pfd; |
| 12609 |  unsigned int i; |
| 12610 |  unsigned int workerthreadcount; |
| 12611 | |
| 12612 |  if (!ctx) { |
| 12613 |    return; |
| 12614 |  } |
| 12615 | |
| 12616 |  mg_set_thread_name("master"); |
| 12617 | |
| 12618 | /* Increase priority of the master thread */ |
| 12619 | #if defined(_WIN32) |
| 12620 |  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); |
| 12621 | #elif defined(USE_MASTER_THREAD_PRIORITY) |
| 12622 |  int min_prio = sched_get_priority_min(SCHED_RR); |
| 12623 |  int max_prio = sched_get_priority_max(SCHED_RR); |
| 12624 |  if ( |
| 12625 |   |
| 12626 |   ) { |
| 12627 |    struct sched_param sched_param = {0}; |
| 12628 |    sched_param.sched_priority = (USE_MASTER_THREAD_PRIORITY); |
| 12629 |    pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param); |
| 12630 |  } |
| 12631 | #endif |
| 12632 | |
| 12633 | /* Initialize thread local storage */ |
| 12634 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12635 |  tls.pthread_cond_helper_mutex = CreateEvent(NULL, FALSE, FALSE, NULL); |
| 12636 | #endif |
| 12637 |  tls.is_master = 1; |
| 12638 |  pthread_setspecific(sTlsKey, &tls); |
| 12639 | |
| 12640 |  if (ctx->callbacks.init_thread) { |
| 12641 |    /* Callback for the master thread (type 0) */ |
| 12642 |    ctx->callbacks.init_thread(ctx, 0); |
| 12643 |  } |
| 12644 | |
| 12645 |  /* Server starts *now* */ |
| 12646 |  ctx->start_time = time(NULL); |
| 12647 | |
| 12648 |  /* Allocate memory for the listening sockets, and start the server */ |
| 12649 |  pfd = |
| 12650 |   (struct pollfd *)mg_calloc(ctx->num_listening_sockets, sizeof(pfd[0])); |
| 12651 |  while (pfd != NULL && ctx->stop_flag == 0) { |
| 12652 |    for (i = 0; i < ctx->num_listening_sockets; i++) { |
| 12653 |      pfd[i].fd = ctx->listening_sockets[i].sock; |
| 12654 |      pfd[i].events = POLLIN; |
| 12655 |    } |
| 12656 | |
| 12657 |    if (poll(pfd, ctx->num_listening_sockets, 200) > 0) { |
| 12658 |      for (i = 0; i < ctx->num_listening_sockets; i++) { |
| 12659 |        /* NOTE(lsm): on QNX, poll() returns POLLRDNORM after the |
| 12660 |         * successful poll, and POLLIN is defined as |
| 12661 |         * (POLLRDNORM | POLLRDBAND) |
| 12662 |         * Therefore, we're checking pfd[i].revents & POLLIN, not |
| 12663 |         * pfd[i].revents == POLLIN. */ |
| 12664 |        if (ctx->stop_flag == 0 && (pfd[i].revents & POLLIN)) { |
| 12665 |          accept_new_connection(&ctx->listening_sockets[i], ctx); |
| 12666 |        } |
| 12667 |      } |
| 12668 |    } |
| 12669 |  } |
| 12670 |  mg_free(pfd); |
| 12671 |  DEBUG_TRACE("%s", "stopping workers"); |
| 12672 | |
| 12673 |  /* Stop signal received: somebody called mg_stop. Quit. */ |
| 12674 |  close_all_listening_sockets(ctx); |
| 12675 | |
| 12676 |  /* Wakeup workers that are waiting for connections to handle. */ |
| 12677 |  pthread_cond_broadcast(&ctx->sq_full); |
| 12678 | |
| 12679 |  /* Wait until all threads finish */ |
| 12680 |  (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 12681 |  while (ctx->running_worker_threads > 0) { |
| 12682 |    (void)pthread_cond_wait(&ctx->thread_cond, &ctx->thread_mutex); |
| 12683 |  } |
| 12684 |  (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 12685 | |
| 12686 |  /* Join all worker threads to avoid leaking threads. */ |
| 12687 |  workerthreadcount = ctx->cfg_worker_threads; |
| 12688 |  for (i = 0; i < workerthreadcount; i++) { |
| 12689 |    if (ctx->workerthreadids[i] != 0) { |
| 12690 |      mg_join_thread(ctx->workerthreadids[i]); |
| 12691 |    } |
| 12692 |  } |
| 12693 | |
| 12694 | #if !defined(NO_SSL) |
| 12695 |  if (ctx->ssl_ctx != NULL) { |
| 12696 |    uninitialize_ssl(ctx); |
| 12697 |  } |
| 12698 | #endif |
| 12699 |  DEBUG_TRACE("%s", "exiting"); |
| 12700 | |
| 12701 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12702 |  CloseHandle(tls.pthread_cond_helper_mutex); |
| 12703 | #endif |
| 12704 |  pthread_setspecific(sTlsKey, NULL); |
| 12705 | |
| 12706 |  /* Signal mg_stop() that we're done. |
| 12707 |   * WARNING: This must be the very last thing this |
| 12708 |   * thread does, as ctx becomes invalid after this line. */ |
| 12709 |  ctx->stop_flag = 2; |
| 12710 | } |
| 12711 | |
| 12712 | |
| 12713 | /* Threads have different return types on Windows and Unix. */ |
| 12714 | #ifdef _WIN32 |
| 12715 | static unsigned __stdcall master_thread(void *thread_func_param) |
| 12716 | { |
| 12717 |  master_thread_run(thread_func_param); |
| 12718 |  return 0; |
| 12719 | } |
| 12720 | #else |
| 12721 | static void * |
| 12722 | master_thread(void *thread_func_param) |
| 12723 | { |
| 12724 |  master_thread_run(thread_func_param); |
| 12725 |  return NULL; |
| 12726 | } |
| 12727 | #endif /* _WIN32 */ |
| 12728 | |
| 12729 | |
| 12730 | static void |
| 12731 | free_context(struct mg_context *ctx) |
| 12732 | { |
| 12733 |  int i; |
| 12734 |  struct mg_handler_info *tmp_rh; |
| 12735 | |
| 12736 |  if (ctx == NULL) { |
| 12737 |    return; |
| 12738 |  } |
| 12739 | |
| 12740 |  if (ctx->callbacks.exit_context) { |
| 12741 |    ctx->callbacks.exit_context(ctx); |
| 12742 |  } |
| 12743 | |
| 12744 |  /* All threads exited, no sync is needed. Destroy thread mutex and |
| 12745 |   * condvars |
| 12746 |   */ |
| 12747 |  (void)pthread_mutex_destroy(&ctx->thread_mutex); |
| 12748 |  (void)pthread_cond_destroy(&ctx->thread_cond); |
| 12749 |  (void)pthread_cond_destroy(&ctx->sq_empty); |
| 12750 |  (void)pthread_cond_destroy(&ctx->sq_full); |
| 12751 | |
| 12752 |  /* Destroy other context global data structures mutex */ |
| 12753 |  (void)pthread_mutex_destroy(&ctx->nonce_mutex); |
| 12754 | |
| 12755 | #if defined(USE_TIMERS) |
| 12756 |  timers_exit(ctx); |
| 12757 | #endif |
| 12758 | |
| 12759 |  /* Deallocate config parameters */ |
| 12760 |  for (i = 0; i < NUM_OPTIONS; i++) { |
| 12761 |    if (ctx->config[i] != NULL) { |
| 12762 | #if defined(_MSC_VER) |
| 12763 | #pragma warning(suppress : 6001) |
| 12764 | #endif |
| 12765 |      mg_free(ctx->config[i]); |
| 12766 |    } |
| 12767 |  } |
| 12768 | |
| 12769 |  /* Deallocate request handlers */ |
| 12770 |  while (ctx->handlers) { |
| 12771 |    tmp_rh = ctx->handlers; |
| 12772 |    ctx->handlers = tmp_rh->next; |
| 12773 |    mg_free(tmp_rh->uri); |
| 12774 |    mg_free(tmp_rh); |
| 12775 |  } |
| 12776 | |
| 12777 | #ifndef NO_SSL |
| 12778 |  /* Deallocate SSL context */ |
| 12779 |  if (ctx->ssl_ctx != NULL) { |
| 12780 |    SSL_CTX_free(ctx->ssl_ctx); |
| 12781 |  } |
| 12782 | #endif /* !NO_SSL */ |
| 12783 | |
| 12784 |  /* Deallocate worker thread ID array */ |
| 12785 |  if (ctx->workerthreadids != NULL) { |
| 12786 |    mg_free(ctx->workerthreadids); |
| 12787 |  } |
| 12788 | |
| 12789 |  /* Deallocate the tls variable */ |
| 12790 |  if (mg_atomic_dec(&sTlsInit) == 0) { |
| 12791 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12792 |    DeleteCriticalSection(&global_log_file_lock); |
| 12793 | #endif /* _WIN32 && !__SYMBIAN32__ */ |
| 12794 | #if !defined(_WIN32) |
| 12795 |    pthread_mutexattr_destroy(&pthread_mutex_attr); |
| 12796 | #endif |
| 12797 | |
| 12798 |    pthread_key_delete(sTlsKey); |
| 12799 |  } |
| 12800 | |
| 12801 |  /* deallocate system name string */ |
| 12802 |  mg_free(ctx->systemName); |
| 12803 | |
| 12804 |  /* Deallocate context itself */ |
| 12805 |  mg_free(ctx); |
| 12806 | } |
| 12807 | |
| 12808 | |
| 12809 | void |
| 12810 | mg_stop(struct mg_context *ctx) |
| 12811 | { |
| 12812 |  pthread_t mt; |
| 12813 |  if (!ctx) { |
| 12814 |    return; |
| 12815 |  } |
| 12816 | |
| 12817 |  /* We don't use a lock here. Calling mg_stop with the same ctx from |
| 12818 |   * two threads is not allowed. */ |
| 12819 |  mt = ctx->masterthreadid; |
| 12820 |  if (mt == 0) { |
| 12821 |    return; |
| 12822 |  } |
| 12823 | |
| 12824 |  ctx->masterthreadid = 0; |
| 12825 |  ctx->stop_flag = 1; |
| 12826 | |
| 12827 |  /* Wait until mg_fini() stops */ |
| 12828 |  while (ctx->stop_flag != 2) { |
| 12829 |    (void)mg_sleep(10); |
| 12830 |  } |
| 12831 | |
| 12832 |  mg_join_thread(mt); |
| 12833 |  free_context(ctx); |
| 12834 | |
| 12835 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12836 |  (void)WSACleanup(); |
| 12837 | #endif /* _WIN32 && !__SYMBIAN32__ */ |
| 12838 | } |
| 12839 | |
| 12840 | |
| 12841 | static void |
| 12842 | get_system_name(char **sysName) |
| 12843 | { |
| 12844 | #if defined(_WIN32) |
| 12845 | #if !defined(__SYMBIAN32__) |
| 12846 |  char name[128]; |
| 12847 |  DWORD dwVersion = 0; |
| 12848 |  DWORD dwMajorVersion = 0; |
| 12849 |  DWORD dwMinorVersion = 0; |
| 12850 |  DWORD dwBuild = 0; |
| 12851 | |
| 12852 | #ifdef _MSC_VER |
| 12853 | #pragma warning(push) |
| 12854 | // GetVersion was declared deprecated |
| 12855 | #pragma warning(disable : 4996) |
| 12856 | #endif |
| 12857 |  dwVersion = GetVersion(); |
| 12858 | #ifdef _MSC_VER |
| 12859 | #pragma warning(pop) |
| 12860 | #endif |
| 12861 | |
| 12862 |  dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion))); |
| 12863 |  dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion))); |
| 12864 |  dwBuild = ((dwVersion < 0x80000000) ? (DWORD)(HIWORD(dwVersion)) : 0); |
| 12865 |  (void)dwBuild; |
| 12866 | |
| 12867 |  sprintf(name, |
| 12868 |   "Windows %u.%u", |
| 12869 |   (unsigned)dwMajorVersion, |
| 12870 |   (unsigned)dwMinorVersion); |
| 12871 |  *sysName = mg_strdup(name); |
| 12872 | #else |
| 12873 |  *sysName = mg_strdup("Symbian"); |
| 12874 | #endif |
| 12875 | #else |
| 12876 |  struct utsname name; |
| 12877 |  memset(&name, 0, sizeof(name)); |
| 12878 |  uname(&name); |
| 12879 |  *sysName = mg_strdup(name.sysname); |
| 12880 | #endif |
| 12881 | } |
| 12882 | |
| 12883 | |
| 12884 | struct mg_context * |
| 12885 | mg_start(const struct mg_callbacks *callbacks, |
| 12886 | void *user_data, |
| 12887 | const char **options) |
| 12888 | { |
| 12889 |  struct mg_context *ctx; |
| 12890 |  const char *name, *value, *default_value; |
| 12891 |  int idx, ok, workerthreadcount; |
| 12892 |  unsigned int i; |
| 12893 |  void (*exit_callback)(const struct mg_context *ctx) = 0; |
| 12894 | |
| 12895 |  struct mg_workerTLS tls; |
| 12896 | |
| 12897 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12898 |  WSADATA data; |
| 12899 |  WSAStartup(MAKEWORD(2, 2), &data); |
| 12900 | #endif /* _WIN32 && !__SYMBIAN32__ */ |
| 12901 | |
| 12902 |  /* Allocate context and initialize reasonable general case defaults. */ |
| 12903 |  if ((ctx = (struct mg_context *)mg_calloc(1, sizeof(*ctx))) == NULL) { |
| 12904 |    return NULL; |
| 12905 |  } |
| 12906 | |
| 12907 |  /* Random number generator will initialize at the first call */ |
| 12908 |  ctx->auth_nonce_mask = |
| 12909 |   (uint64_t)get_random() ^ (uint64_t)(ptrdiff_t)(options); |
| 12910 | |
| 12911 |  if (mg_atomic_inc(&sTlsInit) == 1) { |
| 12912 | |
| 12913 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12914 |    InitializeCriticalSection(&global_log_file_lock); |
| 12915 | #endif /* _WIN32 && !__SYMBIAN32__ */ |
| 12916 | #if !defined(_WIN32) |
| 12917 |    pthread_mutexattr_init(&pthread_mutex_attr); |
| 12918 |    pthread_mutexattr_settype(&pthread_mutex_attr, PTHREAD_MUTEX_RECURSIVE); |
| 12919 | #endif |
| 12920 | |
| 12921 |    if (0 != pthread_key_create(&sTlsKey, tls_dtor)) { |
| 12922 |      /* Fatal error - abort start. However, this situation should |
| 12923 |       * never |
| 12924 |       * occur in practice. */ |
| 12925 |      mg_atomic_dec(&sTlsInit); |
| 12926 |      mg_cry(fc(ctx), "Cannot initialize thread local storage"); |
| 12927 |      mg_free(ctx); |
| 12928 |      return NULL; |
| 12929 |    } |
| 12930 |  } else { |
| 12931 |    /* TODO (low): istead of sleeping, check if sTlsKey is already |
| 12932 |     * initialized. */ |
| 12933 |    mg_sleep(1); |
| 12934 |  } |
| 12935 | |
| 12936 |  tls.is_master = -1; |
| 12937 |  tls.thread_idx = (unsigned)mg_atomic_inc(&thread_idx_max); |
| 12938 | #if defined(_WIN32) && !defined(__SYMBIAN32__) |
| 12939 |  tls.pthread_cond_helper_mutex = NULL; |
| 12940 | #endif |
| 12941 |  pthread_setspecific(sTlsKey, &tls); |
| 12942 | |
| 12943 | #if defined(USE_LUA) |
| 12944 |  lua_init_optional_libraries(); |
| 12945 | #endif |
| 12946 | |
| 12947 |  ok = 0 == pthread_mutex_init(&ctx->thread_mutex, &pthread_mutex_attr); |
| 12948 |  ok &= 0 == pthread_cond_init(&ctx->thread_cond, NULL); |
| 12949 |  ok &= 0 == pthread_cond_init(&ctx->sq_empty, NULL); |
| 12950 |  ok &= 0 == pthread_cond_init(&ctx->sq_full, NULL); |
| 12951 |  ok &= 0 == pthread_mutex_init(&ctx->nonce_mutex, &pthread_mutex_attr); |
| 12952 |  if (!ok) { |
| 12953 |    /* Fatal error - abort start. However, this situation should never |
| 12954 |     * occur in practice. */ |
| 12955 |    mg_cry(fc(ctx), "Cannot initialize thread synchronization objects"); |
| 12956 |    mg_free(ctx); |
| 12957 |    pthread_setspecific(sTlsKey, NULL); |
| 12958 |    return NULL; |
| 12959 |  } |
| 12960 | |
| 12961 |  if (callbacks) { |
| 12962 |    ctx->callbacks = *callbacks; |
| 12963 |    exit_callback = callbacks->exit_context; |
| 12964 |    ctx->callbacks.exit_context = 0; |
| 12965 |  } |
| 12966 |  ctx->user_data = user_data; |
| 12967 |  ctx->handlers = NULL; |
| 12968 | |
| 12969 | #if defined(USE_LUA) && defined(USE_WEBSOCKET) |
| 12970 |  ctx->shared_lua_websockets = 0; |
| 12971 | #endif |
| 12972 | |
| 12973 |  while (options && (name = *options++) != NULL) { |
| 12974 |    if ((idx = get_option_index(name)) == -1) { |
| 12975 |      mg_cry(fc(ctx), "Invalid option: %s", name); |
| 12976 |      free_context(ctx); |
| 12977 |      pthread_setspecific(sTlsKey, NULL); |
| 12978 |      return NULL; |
| 12979 |    } else if ((value = *options++) == NULL) { |
| 12980 |      mg_cry(fc(ctx), "%s: option value cannot be NULL", name); |
| 12981 |      free_context(ctx); |
| 12982 |      pthread_setspecific(sTlsKey, NULL); |
| 12983 |      return NULL; |
| 12984 |    } |
| 12985 |    if (ctx->config[idx] != NULL) { |
| 12986 |      mg_cry(fc(ctx), "warning: %s: duplicate option", name); |
| 12987 |      mg_free(ctx->config[idx]); |
| 12988 |    } |
| 12989 |    ctx->config[idx] = mg_strdup(value); |
| 12990 |    DEBUG_TRACE("[%s] -> [%s]", name, value); |
| 12991 |  } |
| 12992 | |
| 12993 |  /* Set default value if needed */ |
| 12994 |  for (i = 0; config_options[i].name != NULL; i++) { |
| 12995 |    default_value = config_options[i].default_value; |
| 12996 |    if (ctx->config[i] == NULL && default_value != NULL) { |
| 12997 |      ctx->config[i] = mg_strdup(default_value); |
| 12998 |    } |
| 12999 |  } |
| 13000 | |
| 13001 | #if defined(NO_FILES) |
| 13002 |  if (ctx->config[DOCUMENT_ROOT] != NULL) { |
| 13003 |    mg_cry(fc(ctx), "%s", "Document root must not be set"); |
| 13004 |    free_context(ctx); |
| 13005 |    pthread_setspecific(sTlsKey, NULL); |
| 13006 |    return NULL; |
| 13007 |  } |
| 13008 | #endif |
| 13009 | |
| 13010 |  get_system_name(&ctx->systemName); |
| 13011 | |
| 13012 |  /* NOTE(lsm): order is important here. SSL certificates must |
| 13013 |   * be initialized before listening ports. UID must be set last. */ |
| 13014 |  if (!set_gpass_option(ctx) || |
| 13015 | #if !defined(NO_SSL) |
| 13016 |   !set_ssl_option(ctx) || |
| 13017 | #endif |
| 13018 |   !set_ports_option(ctx) || |
| 13019 | #if !defined(_WIN32) |
| 13020 |   !set_uid_option(ctx) || |
| 13021 | #endif |
| 13022 |   !set_acl_option(ctx)) { |
| 13023 |    free_context(ctx); |
| 13024 |    pthread_setspecific(sTlsKey, NULL); |
| 13025 |    return NULL; |
| 13026 |  } |
| 13027 | |
| 13028 | #if !defined(_WIN32) && !defined(__SYMBIAN32__) |
| 13029 |  /* Ignore SIGPIPE signal, so if browser cancels the request, it |
| 13030 |   * won't kill the whole process. */ |
| 13031 |  (void)signal(SIGPIPE, SIG_IGN); |
| 13032 | #endif /* !_WIN32 && !__SYMBIAN32__ */ |
| 13033 | |
| 13034 |  workerthreadcount = atoi(ctx->config[NUM_THREADS]); |
| 13035 | |
| 13036 |  if (workerthreadcount > MAX_WORKER_THREADS) { |
| 13037 |    mg_cry(fc(ctx), "Too many worker threads"); |
| 13038 |    free_context(ctx); |
| 13039 |    pthread_setspecific(sTlsKey, NULL); |
| 13040 |    return NULL; |
| 13041 |  } |
| 13042 | |
| 13043 |  if (workerthreadcount > 0) { |
| 13044 |    ctx->cfg_worker_threads = ((unsigned int)(workerthreadcount)); |
| 13045 |    ctx->workerthreadids = |
| 13046 |     (pthread_t *)mg_calloc(ctx->cfg_worker_threads, sizeof(pthread_t)); |
| 13047 |    if (ctx->workerthreadids == NULL) { |
| 13048 |      mg_cry(fc(ctx), "Not enough memory for worker thread ID array"); |
| 13049 |      free_context(ctx); |
| 13050 |      pthread_setspecific(sTlsKey, NULL); |
| 13051 |      return NULL; |
| 13052 |    } |
| 13053 |  } |
| 13054 | |
| 13055 | #if defined(USE_TIMERS) |
| 13056 |  if (timers_init(ctx) != 0) { |
| 13057 |    mg_cry(fc(ctx), "Error creating timers"); |
| 13058 |    free_context(ctx); |
| 13059 |    pthread_setspecific(sTlsKey, NULL); |
| 13060 |    return NULL; |
| 13061 |  } |
| 13062 | #endif |
| 13063 | |
| 13064 |  /* Context has been created - init user libraries */ |
| 13065 |  if (ctx->callbacks.init_context) { |
| 13066 |    ctx->callbacks.init_context(ctx); |
| 13067 |  } |
| 13068 |  ctx->callbacks.exit_context = exit_callback; |
| 13069 |  ctx->context_type = 1; /* server context */ |
| 13070 | |
| 13071 |  /* Start master (listening) thread */ |
| 13072 |  mg_start_thread_with_id(master_thread, ctx, &ctx->masterthreadid); |
| 13073 | |
| 13074 |  /* Start worker threads */ |
| 13075 |  for (i = 0; i < ctx->cfg_worker_threads; i++) { |
| 13076 |    (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 13077 |    ctx->running_worker_threads++; |
| 13078 |    (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 13079 |    if (mg_start_thread_with_id(worker_thread, |
| 13080 |     ctx, |
| 13081 |     &ctx->workerthreadids[i]) != 0) { |
| 13082 |      (void)pthread_mutex_lock(&ctx->thread_mutex); |
| 13083 |      ctx->running_worker_threads--; |
| 13084 |      (void)pthread_mutex_unlock(&ctx->thread_mutex); |
| 13085 |      if (i > 0) { |
| 13086 |        mg_cry(fc(ctx), |
| 13087 |         "Cannot start worker thread %i: error %ld", |
| 13088 |         i + 1, |
| 13089 |         (long)ERRNO); |
| 13090 |      } else { |
| 13091 |        mg_cry(fc(ctx), |
| 13092 |         "Cannot create threads: error %ld", |
| 13093 |         (long)ERRNO); |
| 13094 |        free_context(ctx); |
| 13095 |        pthread_setspecific(sTlsKey, NULL); |
| 13096 |        return NULL; |
| 13097 |      } |
| 13098 |      break; |
| 13099 |    } |
| 13100 |  } |
| 13101 | |
| 13102 |  pthread_setspecific(sTlsKey, NULL); |
| 13103 |  return ctx; |
| 13104 | } |
| 13105 | |
| 13106 | |
| 13107 | /* Feature check API function */ |
| 13108 | unsigned |
| 13109 | mg_check_feature(unsigned feature) |
| 13110 | { |
| 13111 |  static const unsigned feature_set = 0 |
| 13112 | /* Set bits for available features according to API documentation. |
| 13113 | * This bit mask is created at compile time, according to the active |
| 13114 | * preprocessor defines. It is a single const value at runtime. */ |
| 13115 | #if !defined(NO_FILES) |
| 13116 |   | 0x0001u |
| 13117 | #endif |
| 13118 | #if !defined(NO_SSL) |
| 13119 |   | 0x0002u |
| 13120 | #endif |
| 13121 | #if !defined(NO_CGI) |
| 13122 |   | 0x0004u |
| 13123 | #endif |
| 13124 | #if defined(USE_IPV6) |
| 13125 |   | 0x0008u |
| 13126 | #endif |
| 13127 | #if defined(USE_WEBSOCKET) |
| 13128 |   | 0x0010u |
| 13129 | #endif |
| 13130 | #if defined(USE_LUA) |
| 13131 |   | 0x0020u |
| 13132 | #endif |
| 13133 | #if defined(USE_DUKTAPE) |
| 13134 |   | 0x0040u |
| 13135 | #endif |
| 13136 | #if !defined(NO_CACHING) |
| 13137 |   | 0x0080u |
| 13138 | #endif |
| 13139 | |
| 13140 | /* Set some extra bits not defined in the API documentation. |
| 13141 | * These bits may change without further notice. */ |
| 13142 | #if defined(MG_LEGACY_INTERFACE) |
| 13143 |   | 0x8000u |
| 13144 | #endif |
| 13145 | #if defined(MEMORY_DEBUGGING) |
| 13146 |   | 0x0100u |
| 13147 | #endif |
| 13148 | #if defined(USE_TIMERS) |
| 13149 |   | 0x0200u |
| 13150 | #endif |
| 13151 | #if !defined(NO_NONCE_CHECK) |
| 13152 |   | 0x0400u |
| 13153 | #endif |
| 13154 | #if !defined(NO_POPEN) |
| 13155 |   | 0x0800u |
| 13156 | #endif |
| 13157 |   ; |
| 13158 |  return (feature & feature_set); |
| 13159 | }
|